I have a csv that I'd like to tokenize line by line with StringStream. The key is that I know apriori what the columns would look like. For example, say I know the file looks like the following
StrHeader,IntHeader
abc,123
xyz,456
I know ahead of time it is a string column, followed by an int column.
Common approach is to read the file line by line
std::string line;
stringstream lineStream;
while (getline(infile, line)) // read line by line
{
cout << "line " << line << endl;
lineStream << line;
string token;
while(getline(lineStream, token, ',')) // push into vector? this is not ideal
{
}
I know I can have 2 loops, and have inner loop tokenizes the string based on commas. Lots of sample code on stackoverflow would store the result into a vector<string>.
I don't want to do create a new vector every line. Since I know apriori what columns the file would have, can I somehow read directly into a string and int variable? Like this
std::string line;
stringstream lineStream;
while (getline(infile, line)) // read line by line
{
cout << "line " << line << endl;
lineStream << line; // DOESNT WORK - tell lineStream we have comma delimited string
string strValue;
int intValue;
lineStream >> strValue >> intValue; // SO MUCH CLEANER
// call foo(strValue, intValue);
}
The problem above is this line
lineStream << line; // DOESNT WORK - tell lineStream we have comma delimited string
From what I could tell, the above code works if the input line is space delimited, not comma delimited.
I have no control over the input. So, simply replacing the "spaces" with "commas" in the original string is not an ideal solution since I don't know if the input already has spaces.
Any ideas? thanks
You could try to only read to the delimiter with std::getline() and then put that in a string stream for conversion.
while (!infile.eof()){
std::getline(infile, strValue, ',');
std::getline(infile, line);
strstr.str(line);
strstr.clear();
int intValue;
strstr >> intValue;
foo(strValue, intValue);
}
Related
I have file1.txt with this information
4231650|A|4444
4225642|A|5555
I checked the code here for how to read pipe delimited file in C++
C++ Read file line by line then split each line using the delimiter
I modified the code a little bit per my needs. The problem is that it reads the first pipe fine but afterwards how do I read rest of the values?
This is my code:
std::ifstream file("file1.txt");
std::string line;
while(std::getline(file, line))
{
std::stringstream linestream(line);
std::string data;
std::string valStr1;
std::string valStr2;
std::getline(linestream, data, '|'); // read up-to the first pipe
// Read rest of the pipe values? Why did the accepted answer worked for int but not string???
linestream >> valStr1 >> valStr2;
cout << "data: " << data << endl;
cout << "valStr1: " << valStr1 << endl;
cout << "valStr2: " << valStr2 << endl;
}
Here's the output:
Code Logic starts here ...
data: 4231650
valStr1: A|4444
valStr2: A|4444
data: 4225642
valStr1: A|5555
valStr2: A|5555
Existing ...
Why did the accepted answer worked for int but not string?
Because | is not a digit and is an implicit delimiter for int numbers. But it is a good char for a string.
Continue doing it in the same way
std::getline(linestream, data, '|');
std::getline(linestream, varStr1, '|');
std::getline(linestream, varStr2);
I've attempted to use atof() (which I think is way off) and stringstream. I feel like stringstream is the answer, but I'm so unfamiliar with it. Based on some Google searches, YouTube videos, and some time at cplusplus.com, my syntax looks like below. I'm pulling data from a .csv file and attempting to put it into a std::vector<double>:
while (file.good() )
{
getline(file,line,',');
stringstream convert (line);
convert = myvector[i];
i++;
}
If you are reading doubles from a stream (file) we can simplify this:
double value;
while(file >> value) {
myvector.push_back(value);
}
The operator>> will read from a stream into the type you want and do the conversion automatically (if the conversions exists).
You could use a stringstream as an intermediate if each line had more information on it. Like a word an integer and a double.
std::string line;
while(std::getline(file, line)) {
std::stringstream lineStream(line);
std::string word;
int integer;
double real;
lineStream >> word >> integer >> real;
}
But this is overkill if you just have a single number on each line.
Now lets look at a csv file.
This is a line based file but each value is seporated by ,. So here you would read a line then loop over that line and read the value followed by the comma.
std::string line;
while(std::getline(file, line)) {
std::stringstream lineStream(line);
double value;
char comma;
while(lineStream >> value) {
// You successfully read a value
if (!(lineStream >> comma && comma == ',')) {
break; // No comma so this line is finished break out of the loop.
}
}
}
Don't put a test for good() in the while condition.
Why is iostream::eof inside a loop condition considered wrong?
Also worth a read:
How can I read and parse CSV files in C++?
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++;
}
Check this program
ifstream filein("Hey.txt");
filein.getline(line,99);
cout<<line<<endl;
filein.getline(line,99);
cout<<line<<endl;
filein.close();
The file Hey.txt has alot of characters in it. Well over a 1000
But my question is
Why in the second time i try to print line. It doesnt get print?
The idiomatic way to read lines from a stream is this:
std::ifstream filein("Hey.txt");
for (std::string line; std::getline(filein, line); )
{
std::cout << line << std::endl;
}
Notes:
No close(). C++ takes care of resource management for you when used idiomatically.
Use the free std::getline, not the stream member function.
According to the C++ reference (here) getline sets the ios::fail when count-1 characters have been extracted. You would have to call filein.clear(); in between the getline() calls.
#include<iostream>
using namespace std;
int main()
{
ifstream in;
string lastLine1;
string lastLine2;
in.open("input.txt");
while(in.good()){
getline(in,lastLine1);
getline(in,lastLine2);
}
in.close();
if(lastLine2=="")
cout<<lastLine1<<endl;
else
cout<<lastLine2<<endl;
return 0;
}
As Kerrek SB said correctly There is 2 possibilities:
1) Second line is an empty line
2) there is no second line and all more than 1000 character is in one line, so second getline has nothing to get.
An easier way to get a line is to use the extractor operator of ifstream
string result;
//line counter
int line=1;
ifstream filein("Hey.txt");
while(filein >> result)
{
//display the line number and the result string of reading the line
cout << line << result << endl;
++line;
}
One problem here though is that it won't work when the line have a space ' ' because it is considered a field delimiter in ifstream. If you want to implement this kind of solution change your field delimiter to e.g. - / or any other field delimiter you like.
If you know how many spaces there is you can eat all the spaces by using other variables in the extractor operator of ifstream. Consider the file has contents of first name last name.
//file content is: FirstName LastName
int line=1;
ifstream filein("Hey.txt");
string firstName;
string lastName;
while(filein>>firstName>>lastName)
{
cout << line << firstName << lastName << endl;
}
I know about getline() but it would be nice if cin could return \n when encountered.
Any way for achieving this (or similar)?
edit (example):
string s;
while(cin>>s){
if(s == "\n")
cout<<"newline! ";
else
cout<<s<<" ";
}
input file txt:
hola, em dic pere
caram, jo també .
the end result shoud be like:
hola, em dic pere newline! caram, jo també .
If you are reading individual lines, you know that there is a newline after each read line. Well, except for the last line in the file which doesn't have to be delimited by a newline character for the read to be successful but you can detect if there is newline by checking eof(): if std::getline() was successful but eof() is set, the last line didn't contain a newline. Obviously, this requires the use of the std::string version of std::getline():
for (std::string line; std::getline(in, line); )
{
std::cout << line << (in.eof()? "": "\n");
}
This should write the stream to std::cout as it was read.
The question asked for the data to be output but with newlines converted to say "newline!". You can achieve this with:
for (std::string line; std::getline(in, line); )
{
std::cout << line << (in.eof()? "": "newline! ");
}
If you don't care about the stream being split into line but actually just want to get the entire file (including all newlines), you can just read the stream into a std::string:
std::string file((std::istreambuf_iterator<char>(in)),
std::istreambuf_iterator<char>());
Note, however, that this exact approach is probably fairly slow (although I know that it can be made fast). If you know that the file doesn't contain a certain character, you can also use std::getline() to read the entire file into a std::string:
std::getline(in, file, 0);
The above code assumes that your file doesn't contain any null characters.
A modification of #Dietmar's answer should do the trick:
for (std::string line; std::getline(in, line); )
{
std::istringstream iss(line);
for (std::string word; iss >> word; ) { std::cout << word << " "; }
if (in.eof()) { std::cout << "newline! "; }
}
Just for the record, I ended up using this (I wanted to post it 11h ago)
string s0, s1;
while(getline(cin,s0)){
istringstream is(s0);
while(is>>s1){
cout<<s1<<" ";
}
cout<<"newline! ";
}