once again I ask for help. I haven't coded anything for sometime!
Now I have a text file filled with random gibberish. I already have a basic idea on how I will count the number of occurrences per word.
What really stumps me is how I will determine what line the word is in. Gut instinct tells me to look for the newline character at the end of each line. However I have to do this while going through the text file the first time right? Since if I do it afterwords it will do no good.
I already am getting the words via the following code:
vector<string> words;
string currentWord;
while(!inputFile.eof())
{
inputFile >> currentWord;
words.push_back(currentWord);
}
This is for a text file with no set structure. Using the above code gives me a nice little(big) vector of words, but it doesn't give me the line they occur in.
Would I have to get the entire line, then process it into words to make this possible?
Use a std::map<std::string, int> to count the word occurrences -- the int is the number of times it exists.
If you need like by line input, use std::getline(std::istream&, std::string&), like this:
std::vector<std::string> lines;
std::ifstream file(...) //Fill in accordingly.
std::string currentLine;
while(std::getline(file, currentLine))
lines.push_back(currentLine);
You can split a line apart by putting it into an std::istringstream first and then using operator>>. (Alternately, you could cobble up some sort of splitter using std::find and other algorithmic primitaves)
EDIT: This is the same thing as in #dash-tom-bang's answer, but modified to be correct with respect to error handing:
vector<string> words;
int currentLine = 1; // or 0, however you wish to count...
string line;
while (getline(inputFile, line))
{
istringstream inputString(line);
string word;
while (inputString >> word)
words.push_back(pair(word, currentLine));
}
Short and sweet.
vector< map< string, size_t > > line_word_counts;
string line, word;
while ( getline( cin, line ) ) {
line_word_counts.push_back();
map< string, size_t > &word_counts = line_word_counts.back();
istringstream line_is( line );
while ( is >> word ) ++ word_counts[ word ];
}
cout << "'Hello' appears on line 5 " << line_word_counts[5-1]["Hello"]
<< " times\n";
You're going to have to abandon reading into strings, because operator >>(istream&, string&) discards white space and the contents of the white space (== '\n' or != '\n', that is the question...) is what will give you line numbers.
This is where OOP can save the day. You need to write a class to act as a "front end" for reading from the file. Its job will be to buffer data from the file, and return words one at a time to the caller.
Internally, the class needs to read data from the file a block (say, 4096 bytes) at a time. Then a string GetWord() (yes, returning by value here is good) method will:
First, read any white space characters, taking care to increment the object's lineNumber member every time it hits a \n.
Then read non-whitespace characters, putting them into the string object you'll be returning.
If it runs out of stuff to read, read the next block and continue.
If the you hit the end of file, the string you have is the whole word (which may be empty) and should be returned.
If the function returns an empty string, that tells the caller that the end of file has been reached. (Files usually end with whitespace characters, so reading whitespace characters cannot imply that there will be a word later on.)
Then you can call this method at the same place in your code as your cin >> line and the rest of the code doesn't need to know the details of your block buffering.
An alternative approach is to read things a line at a time, but all the read functions that would work for you require you to create a fixed-size buffer to read into beforehand, and if the line is longer than that buffer, you have to deal with it somehow. It could get more complicated than the class I described.
Related
I am working on a coding project where I sort and organize data from a text file, and I cannot get the getline() function to read past the first line.
The idea is to capture the entire line, split it into 3 sections, assign it to an object, then move on. I can do everything except get getline() to work properly, here is the snippet of code I am having trouble with:
ifstream fin;
fin.open("textFile.txt");
while (!fin.eof()) // while loop to grab lines until the end of file is reached
{
getline(fin, line);
fin >> first >> last >> pace; // assigning the data to their respective variables
ClassObject obj(first, last, pace); // creating an object with those variables
ClassVector.push_back(obj); // assignment object to vector
}
This has been the closest I have gotten to reading every line while also sorting the data into a vector, but as I mentioned before, getline() will read line 1, and skip the rest of the file (1000 lines).
what you can do is rather than using !fin.eof(). i prefer to use something similar to this:
ifstream file ( fileName.c_str() );
while (file >> first >> last >> pace ) // assuming the file is delimited with spaces
{
// Do whatever you want with first, last, and pace
}
The "While loop" will keep reading the next line until we reach the end of the file.
If the lengths of first, last, pace are constant, you can also just get the contents of the line (in a string variable) and use substring on it, but this only works in the specific case that the lengths are constant throughout the entire file.
Basically my issue is that I'm trying to read in data from a .txt file that's full of numbers and comments and store each line into a string vector, but my getline function stops reading at the first whitespace character so a comment like (* comment *) gets broken up into
str[0] = "(*";
str[1] = "comment";
str[2] = "*)";
This is what my codeblock for the getline function looks like:
int main() {
string line;
string fileName;
cout << "Enter the name of the file to be read: ";
cin >> fileName;
ifstream inFile{fileName};
istream_iterator<string> infile_begin {inFile};
istream_iterator<string> eof{};
vector<string> data {infile_begin, eof};
while (getline(inFile, line))
{
data.push_back(line);
}
And this is what the .txt file looks like:
101481
10974
1013
(* comment *) 0
28292
35040
35372
0000
7155
7284
96110
26175
I can't figure out why it's not reading the whole line.
This is for the very simple reason that your code is not using std::getline to read the input file.
If you look at your code very carefully, you will see that before you even get to that point, your code constructs an istream_iterator<string> on the file, and by passing it, and the ending istream_iterator<string> value to the vector's constructor, this effectively swallows the entire file, one whitespace-delimited word at a time, into the vector.
And by the time things get around to the getline loop, the entire file has already been read, and the loop does absolutely nothing. Your getline isn't really doing anything, with the current state of affairs.
Get rid of that stuff that involves istream_iterators, completely, and simply let getline do the job it was intended for.
Long story short I need my vector to cout as a single line without creating its own new lines for my program to work correctly. the text file i read into the vector was
laptop#a small computer that fits on your lap#
helmet#protective gear for your head#
couch#what I am sitting on#
cigarette#smoke these for nicotine#
binary#ones and zeros#
motorcycle#two wheeled motorized bike#
oj#orange juice#
test#this is a test#
filled the vector using the loop:
if(myFile.is_open())
{
while(getline(myFile, line, '#'))
{
wordVec.push_back(line);
}
cout << "words added.\n";
}
and printed it using this:
for(int i = 0; i < wordVec.size(); i++)
{
cout << wordVec[i];
}
and it outputs as such:
laptopa small computer that fits on your lap
helmetprotective gear for your head
couchwhat I am sitting on
cigarettesmoke these for nicotine
binaryones and zeros
motorcycletwo wheeled motorized bike
ojorange juice
testthis is a test
my program works if I manually input the words and add them to my data structure but if added from the vector which is filled via text file, half of the program doesnt work. before anyone says asks for a better description of the problem, all I need to know is how to fill the vector so that it will output as a single line.
You code getline(myFile, line, '#') reads everything up to end-of-file or the next '#' into line - that includes any newlines. So, as you read text file content...
laptop#a small computer that fits on your lap#
helmet#protective gear for your head#
...which you could also think of as...
"laptop#a small computer that fits on your lap#\nhelmet#protective gear for your head#"
...line takes on successive values...
"laptop"
"a small computer that fits on your lap"
"\nhelmet"
...etc....
Note the newline in "\nhelmet".
There are many ways to avoid or correct this, such as...
while ((myFile >> std::skipws) and getline(myFile, line, '#'))
...
...or...
if (not line.empty() and line[0] == '\n')
line.erase(0, 1);
...or (as Barry suggests in comments)...
while (getline(myFile, line))
{
std::istringstream iss(line);
std::string field;
while (getline(iss, field, '#'))
...
}
while(getline(myFile, line, '#'))
Here, you told std::getline to use the '#' character instead of a newline, '\n', as a delimiter.
So, this simply means that std::getline will no longer think there's anything special about '\n'. It's just another character that std::getline() will keep reading, looking for the next #.
So, you end up reading newline characters into your individual strings, and then outputing them to std::cout, as part of the strings you've printed.
string numbers;
string fileName = "text.txt";
ifstream inputFile;
inputFile.open(fileName.c_str(),ios_base::in);
inputFile >> numbers;
inputFile.close();
cout << numbers;
And my text.txt file is:
1 2 3 4 5
basically a set of integers separated by tabs.
The problem is the program only reads the first integer in the text.txt file and ignores the rest for some reason. If I remove the tabs between the integers it works fine, but with tabs between them, it won't work. What causes this? As far as I know it should ignore any white space characters or am I mistaken? If so is there a better way to get each of these numbers from the text file?
When reading formatted strings the input operator starts with ignoring leading whitespace. Then it reads non-whitespace characters up to the first space and stops. The non-whitespace characters get stored in the std::string. If there are only whitespace characters before the stream reaches end of file (or some error for that matter), reading fails. Thus, your program reads one "word" (in this case a number) and stops reading.
Unfortunately, you only said what you are doing and what the problems are with your approach (where you problem description failed to cover the case where reading the input fails in the first place). Here are a few things you might want to try:
If you want to read multiple words, you can do so, e.g., by reading all words:
std::vector<std::string> words;
std::copy(std::istream_iterator<std::string>(inputFile),
std::istream_iterator<std::string>(),
std::back_inserter(words));
This will read all words from inputFile and store them as a sequence of std::strings in the vector words. Since you file contains numbers you might want to replace std::string by int to read numbers in a readily accessible form.
If you want to read a line rather than a word you can use std::getline() instead:
if (std::getline(inputFile, line)) { ... }
If you want to read multiple lines, you'd put this operation into a loop: There is, unfortunately, no read-made approach to read a sequence of lines as there is for words.
If you want to read the entire file, not just the first line, into a file, you can also use std::getline() but you'd need to know about one character value which doesn't occur in your file, e.g., the null value:
if (std::getline(inputFile, text, char()) { ... }
This approach considers a "line" a sequence of characters up to a null character. You can use any other character value as well. If you can't be sure about the character values, you can read an entire file using std::string's constructor taking iterators:
std::string text((std::istreambuf_iterator<char>(inputFile)),
std::istreambuf_iterator<char>());
Note, that the extra pair of parenthesis around the first parameter is, unfortunately, necessary (if you are using C++ 2011 you can avoid them by using braces, instead of parenthesis).
Use getline to do the reading.
string numbers;
if (inputFile.is_open())//checking if open
{
getline (inputFile,numbers); //fetches entire line into string numbers
inputFile.close();
}
Your program does behave exactly as in your description : inputFile >> numbers; just extract the first integer in the input file, so if you suppress the tab, inputFile>> will extract the number 12345, not 5 five numbers [1,2,3,4,5].
a better method :
vector< int > numbers;
string fileName = "text.txt";
ifstream inputFile;
inputFile.open(fileName.c_str(),ios_base::in);
char c;
while (inputFile.good()) // loop while extraction from file is possible
{
c = inputFile.get(); // get character from file
if ( inputFile.good() and c!= '\t' and c!=' ' ) // not sure of tab and space encoding in C++
{
numbers.push_back( (int) c);
}
}
inputFile.close();
Basically, I'm trying to read in the words from a file and, without punctuation, read each word into a multimap which is then inserted into a vector with each pair being a word and the line of the file that word is found. I've got the function to remove punctuation working perfectly and I'm fairly certain my insert code works properly, but I can't seem to get around the line number part. I've included this section of my code as follows:
ifstream in("textfile.txt");
string line;
string keys;
stringstream keystream;
int line_number = 1;
while (getline(in, line, '\n')) {
alphanum(line);
keystream << line;
while(getline(keystream, keys, ' '))
table.insert(keys, line_number); //this just inserts the pair into my vector (table is an instance of a class I created)
keystream.str("");
line_number++;
}
The problem seems to be related to the stringstream. It doesn't seem to clear when I use keystream.str(""). This particular method only seems to read line 1 in and then exits the loop, whereas some other variations I've tried (I can't remember exactly what I did) read the entire file but don't flush the stringstream so it reads like word 1, word 1, word 2, word 1, word 2, word 3, etc.. Anyway, if anyone could point me in the right direction or perhaps link to a guide specific to parsing input in c++ that would be greatly appreciated! Thanks!
Don't keep the string stream object; just make a new one in each round:
string line;
while (getline(in, line, '\n'))
{
alphanum(line);
istringstream keystream(line);
string keys;
while (getline(keystream, keys, ' ')) // or even "while (keystream >> keys)"
{
}
}
I think the problem is that the second getline() loop sets the EOF flag on the stringstream, and this is not cleared when you call str(). You need to call .clear() also on 'keystream'.