Custom istream end iterator - c++

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));

Related

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);
}
}
}

Tokenize stringstream

I need cut string stream according custom separator. Current code cuts just acording to several standart separators. How to define and cut stringstream to string line according to custom delimiter?
std::istringstream input;
input.str("1\n2\n3\n4\n5\n6\n7\n");
int sum = 0;
for (std::string line; std::getline(input, line); )
{
cout<<line;
}
If you have one delimiter you want to use and it's a single character, you can just pass it to the 3-parameter overload of std::getline():
std::istringstream input;
input.str("1;2;3;4;5;6;7;");
int sum = 0;
for (std::string field; std::getline(input, field, ';'); )
{
std::cout<<field;
}
Live example
For other situations (multi-character delimiter, multiple delimiters), you might want to consider using Boost.Tokenizer.
Use third argument of overloaded std::getline
for (std::string line; std::getline(input, line, delimiter ); )
{
std::cout<< line <<'\n';
}

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++;
}

Reading a text file line by line in C++

How does the code below work ? where is the counter for the for-loop and how can i reset the counter to line number 0.
for (std::string line;std::getline(ifs, line); )
{
}
There is no need for a counter. This is equivalent to
std::string line;
while(getline(ifs, line))
{
}
There are methods to move the input iterator back to the beginning of the file. Something like: ifs.seekg(0, std::ios::beg); should do the trick.
Your for loop is equivalent to:
{
std::string line;
while (std::getline(ifs, line)) {
}
}
In other words: "keep iterating as long as getline returns true".
And to reset the counter to line number 0 (i.e. to the beginning of stream) you should use
ifs.seekg (0, ios::beg);

Using isstringstream as a tokenizer in a loop

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.