Using isstringstream as a tokenizer in a loop - c++

I would like some help understanding how to deal with isstringstream objects.
I am trying to tokenize each line of a file so I can re-write it in another format after checking certain data values in the tokens. I am loading each line in a tokenVector and iterating through the vector. My code works, but what concerns me is that I have to instantiate a isstringstrem object for each iteration otherwise it does not work. That does not feel right. Her is my code:
std::string line;//each file line
std::ifstream myFile (info.txt.c_str());
if(myFile.is_open()){
getline(myFile, line);
std::vector<std::string> tokenVector;
//create a isstringstream object for tokenizing each line of the file
std::istringstream hasTokens(line);
while(hasTokens)
{
std::string substring;
if(! getline(hasTokens, substring,','))
break;
tokenVector.push_back(substring);
}
//look for some known header names for validation
if(!tokenVector.empty()){
if(!(tokenVector[0]=="Time")&&(tokenVector[1] == "Group")&&(tokenVector[2]=="Perception")&&(tokenVector[3] == "Sign")){
setErrorMesssage("Invalid Header in myFile");
return false;
}
tokenVector.clear();
}
//clear the isstringstream object
hasTokens.str(std::string());
//if header validates, do rest of file
while(myFile.good()){
getline(myFile , line);
//break line into tokens using istringstream
std::istringstream hasTokens(line);
//reload the vector of tokens for each line
while(hasTokens)
{
std::string substring;
if(! getline(hasTokens, substring,','))
break;
tokenVector.push_back(substring);
}
otherFileWritingFunction(tokenVector[0], tokenVector[2], tokenVector[4]);
tokenVector.clear();
hasTokens.str(std::string());
}//end while
}//end if is_open
This code works, but its not correct because I should only have to instantiate isstringstream once (I think). If I try "hasTokens.str(line)" for each iteration using just the original instantiation of hasTokens, as some example have suggested, it does not work, so I would really appreciate a suggestion.
Thanks

Nope, your worries are misplaced. Create a new stream object when you need it, and dispose of it when you're done. That's the spirit of C++. An object for each purpose, and a purpose for each object (misquoting Frank Herbert). There's nothing "expensive" about constructing a string stream that wouldn't also happen when you reassign the string data of an existing string stream.
Your code is very noisy and redundant, though. The standard idiom goes like this:
std::string line;
while (std::getline(infile, line))
{
std::istringstream iss(line);
std::string token;
while (iss >> token) { /* do stuff */ }
}
Compressed version (some would call this abuse):
for (std::string line; std::getline(infile, line); )
{
std::istringstream iss(line);
for (std::string token; iss >> token; ) { /* ... */ }
}

The second std::istringstream declaration has an entirely different scope and is being constructed in each iteration so hasTokens.str(std::string()); has no effect.
You could reuse the same object if you did hasTokens.str(line) in the while loop instead.

Related

Custom istream end iterator

I want to read one line of text from an istream and split it into words. My current code is
vector<string>((istream_iterator<string>(f)),
istream_iterator<string>());
where f is an ifstream object. However, this reads to the end of the file, and I want to stop it from reading when it reaches a newline. Is there any way to do something like istream_iterator<string>("\n") so the vector constructor will stop pushing back when it reaches a newline?
Use std::getline() to read a single line, and then use std::istringstream to read words from that line, eg:
std::string line;
std::getline(f, line);
std::istringstream iss(line);
std::vector<std::string> words(
std::istream_iterator<std::string>(iss),
std::istream_iterator<std::string>()
);
Use std::getline to read each line and push them to your result container
std::vector<std::string> lines;
for (std::string line; std::getline(f, line); lines.push_back(line));
Edit: Remy Lebeau already answered to OP's question, I want to clarify my code:
Normaly, to read lines in a file we would write something like
while (f.good())
{
std::string line;
std::getline(f, line);
lines.push_back(line);
}
Since std::istream can be converted to boolean, we can rewrite it as:
while (f)
{
std::string line;
std::getline(f, line);
lines.push_back(line);
}
And since std::getline return the input stream itself, we can use it as the loop condition:
std::string line;
while (std::getline(f, line))
{
lines.push_back(line);
} // `line` still can be used after this line, outside its scope
But we have to pull the temporary variable line outside of its scope. To avoid doing so we use for instead of while loop and declare line inside for loop:
for (std::string line; std::getline(f, line); )
{
lines.push_back(line);
}
Finally, since the body of the loop contains only 1 line, we can put it inside for loop too:
for (std::string line; std::getline(f, line); lines.push_back(line));

Reading a file into memory C++: Is there a getline() for std::strings

I was asked to update my code that reads in a text file and parses it for specific strings.
Basically instead of opening the text file every time, I want to read the text file into memory and have it for the duration of the object.
I was wondering if there was a similar function to getline() I could use for a std::string like i can for a std::ifstream.
I realize I could just use a while/for loop but I am curious if there is some other way. Here is what I am currently doing:
file.txt: (\n represents a newline )
file.txt
My Code:
ifstream file("/tmp/file.txt");
int argIndex = 0;
std::string arg,line,substring,whatIneed1,whatIneed2;
if(file)
{
while(std::getline(file,line))
{
if(line.find("3421",0) != string::npos)
{
std::getline(file,line);
std::getline(file,line);
std::stringstream ss1(line);
std::getline(file,line);
std::stringstream ss2(line);
while( ss1 >> arg)
{
if( argIndex==0)
{
whatIneed1 = arg;
}
argIndex++;
}
argIndex=0;
while( ss2 >> arg)
{
if( argIndex==0)
{
whatIneed2 = arg;
}
argIndex++;
}
argIndex=0;
}
}
}
Where at the end whatIneed1=="whatIneed1" and whatIneed2=="whatIneed2".
Is there a way to do this with storing file.txt in a std::string instead of a std::ifstream asnd using a function like getline()? I like getline() because it makes getting the next line of the file that much easier.
If you've already read the data into a string, you can use std::stringstream to turn it into a file-like object compatible with getline.
std::stringstream ss;
ss.str(file_contents_str);
std::string line;
while (std::getline(ss, line))
// ...
Rather than grab a line then try to extract one thing from it, why not extract the one thing, then discard the line?
std::string whatIneed1, whatIneed2, ignored;
if(ifstream file("/tmp/file.txt"))
{
for(std::string line; std::getline(file,line);)
{
if(line.find("3421",0) != string::npos)
{
std::getline(file, ignored);
file >> whatIneed1;
std::getline(file, ignored);
file >> whatIneed2;
std::getline(file, ignored);
}
}
}

Error using getline with ifstream - C++

I need help, I tried googling if I could find a similar problem but the solutions for others didn't work for me.
I'm trying to use getline() to read the file I've opened but it's not accepting the parameters I've given it.
What I'm trying to accomplish at this time (not the entire program) is to open a .csv file and determine how many elements it has inside by using getline() and using the , character as the delimiter. My loop has an index which I could just add 1 to it so that I can get the total number of elements inside the file.
The reason I'm doing this is because I intend to use it for a project at school but so far I've gotten stuck at the getline() error:
no matching function for call to 'std::basic_ifstream::getline(std::string&, int, const char [2])'
My code is here:
void readfile(string a)
{
int i = 0;
ifstream infile;
infile.open(a.c_str());
string temp;
//count how many elements are inside
if(infile.is_open())
{
while(infile.good())
{
infile.getline(temp, 256, ",");
i++;
}
infile.close();
i+=1;
}
else
{
cout<<"Error opening file.";
}
cout<<i;
}
Use the free getline() function:
std::string line;
getline(infile, line);
In addition to the answer by #UlrichEckhardt, I'd handle delimiters like this:
if(infile.is_open())
{
string temp;
// std::getline(std;:istream&, std::string) used below
while(getline(infile, temp)) {
std::stringstream stream(str);
std::string token;
while (std::getline(stream, token, ','))
if (!token.empty()) // it's up to you to decide how to handle empty tokens
i++;
}
}
Note the ','. If it were ".", this would be considered a string by the compiler, which is exactly what you're seeing in the error message: a '\0' is appended automatically, thus producing a char[2].

Reading in file with delimiter

How do I read in lines from a file and assign specific segments of that line to the information in structs? And how can I stop at a blank line, then continue again until end of file is reached?
Background: I am building a program that will take an input file, read in information, and use double hashing for that information to be put in the correct index of the hashtable.
Suppose I have the struct:
struct Data
{
string city;
string state;
string zipCode;
};
But the lines in the file are in the following format:
20
85086,Phoenix,Arizona
56065,Minneapolis,Minnesota
85281
56065
Sorry but I still cannot seem to figure this out. I am having a really hard time reading in the file. The first line is basically the size of the hash table to be constructed. The next blank line should be ignored. Then the next two lines are information that should go into the struct and be hashed into the hash table. Then another blank line should be ignored. And finally, the last two lines are input that need to be matched to see if they exist in the hash table or not. So in this case, 85281 is not found. While 56065 is found.
As the other two answers point out you have to use std::getline, but this is how I would do it:
if (std::getline(is, zipcode, ',') &&
std::getline(is, city, ',') &&
std::getline(is, state))
{
d.zipCode = std::stoi(zipcode);
}
The only real change I made is that I encased the extractions within an if statement so you can check if these reads succeeded. Moreover, in order for this to be done easily (you wouldn't want to type the above out for every Data object), you can put this inside a function.
You can overload the >> operator for the Data class like so:
std::istream& operator>>(std::istream& is, Data& d)
{
std::string zipcode;
if (std::getline(is, zipcode, ',') &&
std::getline(is, d.city, ',') &&
std::getline(is, d.state))
{
d.zipCode = std::stoi(zipcode);
}
return is;
}
Now it becomes as simple as doing:
Data d;
if (std::cin >> d)
{
std::cout << "Yes! It worked!";
}
You can use a getline function from <string> like this:
string str; // This will store your tokens
ifstream file("data.txt");
while (getline(file, str, ',') // You can have a different delimiter
{
// Process your data
}
You can also use stringstream:
stringstream ss(line); // Line is from your input data file
while (ss >> str) // str is to store your token
{
// Process your data here
}
It's just a hint. Hope it helps you.
All you need is function std::getline
For example
std::string s;
std::getline( YourFileStream, s, ',' );
To convert a string to int you can use function std::stoi
Or you can read a whole line and then use std::istringstream to extract each data with the same function std::getline. For example
Data d = {};
std::string line;
std::getline( YourFileStream, line );
std::istringstream is( line );
std::string zipCode;
std::getline( is, zipCode, ',' );
d.zipCode = std::stoi( zipCode );
std::getline( is, d.city, ',' );
std::getline( is, d.state, ',' );

How to read a file word by word and find the position of each word?

I'm trying to read a file word by word and do some implementation on each word. In future I want to know where was the position of each word. Position is line number and character position in that line. If character position is not available I only need to know when I'm reading a file when I go to the next line. This is the sample code I have now:
string tmp;
while(fin>>tmp){
mylist.push_back(tmp);
}
I need to know when fin is going to next line?!
"I need to know when fin is going to next line"
This is not possible with stream's operator >>. You can read the input line by line and process each line separately using temporary istringstream object:
std::string line, word;
while (std::getline(fin, line)) {
// skip empty lines:
if (line.empty()) continue;
std::istringstream lineStream(line);
for (int wordPos = 0; lineStream >> word; wordPos++) {
...
mylist.push_back(word);
}
}
just don't forget to #include <sstream>
One simple way to solve this problem would be using std::getline, run your own counter, and split line's content into words using an additional string stream, like this:
string line;
int line_number = 0;
for (;;) {
if (!getline(fin, line)) {
break;
}
istringstream iss(line);
string tmp;
while (iss >> tmp) {
mylist.push_back(tmp);
}
line_number++;
}