read space delimited number from file up to newline character - c++

I have a text file that contains the following data.
The first line is this:
5 4 3 2 1
The second line is this:
1 2 3 4 5
I am trying to read data from one line at a time because my first linked-list object is going to use the data from the first line and my second linked-list object is going to use data from the second line. The best I have been able to come up with is the following function:
void polynomial::allocate_poly(std::ifstream& in, const char* file, const char* number)
{
in.open(file);
std::string str;
char b;
int m = 0;
for(int i = 0; !in.eof(); ++i)
{
in >> b;
m = b - '0';
a.insert(m);
}
There is a few problems with this approach. I have tried different binary operators in my for loop such as b == '\n' and none of them seem to trigger when b is a newline character.
Also allocating the numbers from the file this way it looks like
5 5 4 3 2 1 1 2 3 4 5 , so it seems to be copying an extra 5 somewhere, I am not sure if this is the eof bit or not.
I have also attempted to use the getline function but for some reason it seems to only copy the first integer and then dumps the rest of the file. I know certainly I am not using it correctly but all the examples I can find are for typing the file name such as cin.getline and I want to be able to pass my file name as a command line argument when running the program instead.
My question is how can I allocate the numbers on the first row up to the newline char and then pass the ifstream in variable to another object to allocate the second line? Thanks for your help.

You don't say what a is, but never mind: If you want line based parsing, you need to have getline in the outer loop. Also, never use eof, as it doesn't do what you want. Rather use the implicit conversion to bool to check if an operation succeeded.
Here's the typical gig:
#include <sstream>
#include <fstream>
#include <string>
std::string line;
std::ifstream infile("myfile.txt");
while (std::getline(infile, line)) // this does the checking!
{
std::istringstream iss(line);
char c;
while (iss >> c)
{
int value = c - '0';
// process value
}
}
However, this conversion from char to int is cumbersome and fragile. Why not read an integer directly?
int value;
while (iss >> value) { /* ... */ }
Edit: Based on your comment, here's the code to read exactly two lines:
std::string line;
int val;
if (std::getline(infile, line))
{
std::istringstream iss(line);
while (iss >> value) { /* process first line */ }
}
if (std::getline(infile, line))
{
std::istringstream iss(line);
while (iss >> value) { /* process second line */ }
}

Related

(C++) Reading a CSV text file as a vector of integers

I'm a beginner programmer working through the 2019 Advent of Code challenges in C++.
The last piece of the puzzle I'm putting together is actually getting the program to read the input.txt file, which is essentially a long string of values in the form of '10,20,40,23" etc. on a single line.
In the previous puzzle I used the lines
int inputvalue;
std::ifstream file("input.txt");
while(file >> inputvalue){
//
}
to grab lines from the file, but it was formatted as a text file in sequential lines with no comma separation.
ie:
10
20
40
23
What can I do to read through the file using the comma delineation, and specifically how can I get those values to be read as integers, instead of as strings or chars, and store them into a vector?
While it would be strange to write a routine to read just one line from a comma separated file, instead of writing a general routine to read all lines (and just take the first one if you only wanted one) -- you can take out the parts for reading multiple lines into a std::vector<std::vector<int>> and just read one line into a std::vector<int> -- though it only saves a handful of lines of code.
The general approach would be to read the entire line of text with getline(file, line) and then create a std::stringstream (line) from which you could then use >> to read each integer followed by a getline (file, tmpstr, ',') to read the delimiter.
You can take a second argument in addition to the file to read, so you can pass the delimiter as the first character of the second argument -- that way there is no reason to re-compile your code to handle delimiters of ';' or ',' or any other single character.
You can put a short bit of code together to do that which could look like the following:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
int main (int argc, char **argv) {
if (argc < 2) { /* validate at least 1 argument given */
std::cerr << "error: insufficient number of arguments.\n"
"usage: " << argv[0] << " <filename>\n";
return 1;
}
std::vector<int> v {}; /* vector<int> */
std::string line {}; /* string to hold each line */
std::ifstream f (argv[1]); /* open file-stream with 1st argument */
const char delim = argc > 2 ? *argv[2] : ','; /* delim is 1st char in */
/* 2nd arg (default ',') */
if (!f.good()) { /* validate file open for reading */
std::cerr << "errro: file open failed '" << argv[1] << "'.\n";
return 1;
}
if (getline (f, line)) { /* read line of input into line */
int itmp; /* temporary integer to fill */
std::stringstream ss (line); /* create stringstream from line */
while (ss >> itmp) { /* read integer value from ss */
std::string stmp {}; /* temporary string to hold delim */
v.push_back(itmp); /* add to vector */
getline (ss, stmp, delim); /* read delimiter */
}
}
for (auto col : v) /* loop over each integer */
std::cout << " " << col; /* output col value */
std::cout << '\n'; /* tidy up with newline */
}
(note: there are relatively few changes needed to read all lines into a vector of vectors, the more notable is simply replacing the if(getline...) with while(getline..) and then filling a temporary vector which, if non-empty, is then pushed back into your collection of vectors)
Example Input File
With a set of comma separated integers in the file named dat/int-1-10-1-line.txt, e.g.
$ cat dat/int-1-10-1-line.txt
1,2,3,4,5,6,7,8,9,10
Example Use/Output
Your use an results would be:
$ ./bin/read_csv_int-1-line dat/int-1-10-1-line.txt
1 2 3 4 5 6 7 8 9 10
Of course you can change the output format to whatever you need. Look things over and let me know if you have further questions.
You have options. In my opinion, the most straight-forward is to just read a string and then convert to integer. You can use the additional "delimiter" parameter of std::getline to stop when it encounters a comma:
std::string value;
while (std::getline(file, value, ',')) {
int ival = std::stoi(value);
std::cout << ival << std::endl;
}
A common alternative is to read a single character, expecting it to be a comma:
int ival;
while (file >> ival) {
std::cout << ival << std::endl;
// Skip comma (we hope)
char we_sure_hope_this_is_a_comma;
file >> we_sure_hope_this_is_a_comma;
}
If it's possible for whitespace to also be present, you may want a less "hopeful" technique to skip the comma:
// Skip characters up to (and including) next comma
for (char c; file >> c && c != ',';);
Or simply:
// Skip characters up to (and including) next comma
while (file && file.get() != ',');
Or indeed, if you expect only whitespace or a comma, you could do something like:
// Skip comma and any leading whitespace
(file >> std::ws).get();
Of course, all the above are more-or-less clunky ways of doing this:
// Skip characters up to (and including) next comma on next read
file.ignore(std::numeric_limits<std::streamsize>::max(), ',');
All these approaches assume input is a single line. If you expect multiple lines of input with comma-separated values, you'll also need to handle end-of-line occurring without encountering a comma. Otherwise, you might miss the first input on the next line. Except for the "hopeful" approach, which will work but only on a technicality.
For robustness, I generally advise you to read line-based input as a whole string with std::getline, and then use std::istringstream to read individual values out of that line.
Here is another compact solution using iterators.
#include <iostream>
#include <vector>
#include <string>
#include <iterator>
#include <fstream>
#include <algorithm>
template <char D>
struct WordDelimiter : public std::string
{};
template <char D>
std::istream &
operator>>(std::istream & is, WordDelimiter<D> & output)
{
// Output gets every comma-separated token
std::getline(is, output, D);
return is;
}
int main() {
// Open a test file with comma-separated tokens
std::ifstream f{"test.txt"};
// every token is appended in the vector
std::vector<std::string> vec{ std::istream_iterator<WordDelimiter<','>>{ f },
std::istream_iterator<WordDelimiter<','>>{} };
// Transform str vector to int vector
// WARNING: no error checking made here
std::vector<int> vecint;
std::transform(std::begin(vec),std::end(vec),std::back_inserter(vecint),[](const auto& s) { return std::stoi(s); });
for (auto val : vecint) {
std::cout << val << std::endl;
}
return 0;
}

counting lines from file not using getline()

I want to get some data from file and put it to variables. But before, I don't know the length of this file. File looks like:
1 12 4
2 14 5
3 26 6
. .. .
That's why I want to get a number of lines to know, how much variables I need to create in loop. In this code I created 3 variables (a,b,c) to try. The problem is that after the end of program a,b,c are just random numbers instead of 1, 12 and 4.
I noticed that if I put this:
file >> a >> b >> c;
before while loop it works, but I need number of lines earlier.
So how to count lines not using getline() ?
int a, b, c;
fstream file;
file.open("abc.txt",ios::in);
if (file.good() == true)
{
int lines_amount=0;
string test;
while (getline(file,test))
{
lines_amount++;
}
file >> a >> b >> c;
file.close();
}
When you use
while (getline(file,test))
{
lines_amount++;
}
the while loop stops only after everything from the file has been read. The line
file >> a >> b >> c;
does not read anything into a, b, or c. The values of those variables are some random values that you get since they have not been initialized before.
Initialize them to something like:
int a = 10, b = 20, c = 30;
and you will notice that:
If you use a pre-C++11 compiler, the values of those variable remain unchanged.
If you use a C++11 or later compiler, the values of those variables will be set to 0.
To be able to read the numbers from the start of the file, rewind it to the top. You can use std::ifstream::seekg for that. You have to clear its eofbit first though.
file.clear();
file.seekg(std::ifstream::beg);
What about rewinding the file indicator?
file.seekg(std::ifstream::beg);
In the previous while loop, the whole file has already been read, so there's nothing left for the next read, and a, b and c remain untouched. After this code, the file indicator is reset to the beginning of the file, so you're able to read from start again.
You may need to add file.clear() before calling seekg() to clear any flags that are already set, to prevent further operations from failing. Most of them don't do anything if a bad flag is set. In your case, when attempting to read more, the operator >> () function finds a bad flag (std::ios_base::iostate::eofbit) and stops. Further Reference
That's why i want to get a number of lines to know, how much variables i need to create in loop.
You don't need to know the number of lines beforehand. Use a std::vector to store the values, and let it grow dynamically as you add values into it. For example:
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
struct values {
int a, b, c;
};
std::vector<values> data;
std::ifstream file("abc.txt");
if (file.is_open()) {
std::string line;
while (std::getline(file, line)) {
std::istringstream iss(line);
values v;
if (iss >> v.a >> v.b >> v.c) {
data.push_back(v);
}
}
file.close();
// use data as needed...
}
Alternatively:
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <algorithm>
struct values {
int a, b, c;
};
std::istream& operator>>(std::istream &in, values &v) {
std::string line;
if (std::getline(in, line)) {
std::istringstream iss(line);
if (!(iss >> v.a >> v.b >> v.c)) {
in.setstate(std::ios_base::failbit);
}
}
return in;
}
std::vector<values> data;
ifstream file("abc.txt");
if (file.is_open()) {
std::copy(
std::istream_iterator<values>(file),
std::istream_iterator<values>(),
std::back_inserter(data));
file.close();
// use data as needed...
}

Pushing a vector into another vector

I have a file that goes like this:
98
287 2352
987 4313 3253
235 34325 432 123
Basically I want to reproduce this file. I am trying to import a line at a time, extract the numbers, and push them into vectors. Each vector is pushed into a larger vector.
int main(int argc, char* argv[])
{
int sum = 0, one = 0, two = 1;
std::string line, number;
std::vector<int> vec;
std::vector<std::vector<int>> bigvec;
auto k = vec.begin();
std::ifstream in(argv[1]);
while(in >> line) {
std::istringstream is(line);
while(is >> number) {
vec.push_back(stoi(number));
}
bigvec.push_back(vec);
while(!vec.empty()) {
vec.pop_back();
}
std::cout << std::endl;
}
return 0;
}
My code though, when I print the result, seems to put each number in it's own vector instead of reproducing the file.
So my output is
98
287
2352
etc.
It seems that the line
while(is >> number) {
vec.push_back(stoi(number));
}
pushes one number and then exits the loop.
Where am I going wrong?
while(in >> line) reads next word from the input. Use getline(in, line) if you want to read a whole line.
There are multiple optimizations that you can add to your code. For instance instead of using stoi on the string you've read you can read an integer from the input stream. Also instead of popping the vector's elements one by one you can simply call clear.
Your problem lies here:
while(in >> line)
C++ by defult reads until it encounters interval or new line. In your case it encounters interval before new line. If you want to take the whole line take:
getline(cin, line);

Reading/parsing text file input c++

A little background: I am working on a sliding block puzzle for a school project and this is our first using C++ instead of Java. This is the first time I have had to implement something that reads data from a file.
I have a simple question regarding reading input from a text file.
I understand how to read the file line by line and hold each line in a string, I want to know if I can parse the string into different data types as the file is read.
Currently I am reading each line and storing them as strings in a vector for parsing later, and I know there must be a much simpler way to implement this
The first line holds 2 integers which will indicate the length and width of the grid, the following lines will have 4 integers and a char for use as arguments when creating the blocks.
My question is this, if I read the file character by character instead, is there a function I can use that will detect if the character is an integer or a char (and ignore the spaces) so I can store them immediately and create the block objects as the file is read? How would i deal with integers >10 in this case?
EDIT: Just noting I am using fstream to read the files, I am unfamiliar with other input methods
A sample input:
4 4
3 1 2 1 b
1 1 1 1 a
To detect whether a piece of string can be parsed as an integer, you just have to parse it and see if you succeed. The best function for that would probably be std::strtoul(), since it can be made to tell you how many characters it consumed, so that you can continue parsing after that. (See the man page for details.)
However, if you already know the format of your file, you can use iostream formatted extraction. This is quite straightforward:
#include <fstream>
std::ifstream infile("thefile.txt");
int n1, n2, x1, x2, x3, x4;
char c;
if (!(infile >> n1 >> n2)) { /* error, could not read first line! Abort. */ }
while (infile >> x1 >> x2 >> x3 >> x4 >> c)
{
// successfully extracted one line, data is in x1, ..., x4, c.
}
Another alternative is to read every line into a string (using std::getline), then creating a stringstream from that line, and parsing the stringstream with >>. This has the added benefit that you can discover and skip bad lines and recover, while in the direct formatted extraction I presented above, you cannot recover from any error.
If you can assert each type, I suggest using stream operators, like you would with cin.
#include <fstream>
using namespace std;
int main()
{
fstream fileStream;
fileStream.open("inputfile.txt");
int firstNumber;
fileStream >> firstNumber;
char firstChar;
fileStream >> firstChar;
}
This way, you can read by value, instead of reading by line and then parsing the line. Just read in every value you need into a variable, as you discover you need it, like that.
I would read each line into a string (as you have been doing).
Then I would read the tokens from that line into the appropriate variables.
The operator>> when applied to a stream will convert the next value in a stream into the correct type. If this is not possable it sets flags on the stream indicating failure that are easy to test.
int x;
stream >> x; // ignores white space then: reads an integer from the stream into x
char c;
stream >> c; // ignores white space then: reads an char from the stream into c
double d;
stream >> d; // ignores white space then: reads an double from the stream into d
Assuming your input:
4 4
3 1 2 1 b
1 1 1 1 a
Not knowing what the the values mean I will put my assumptions in comments.
// Assume that stream is a std::fstream already opened to you file.
std::string line1;
std::getline(stream, line1); // reads "4 4" into `line1`
std::stringstream line1stream(line1); // convert line1 into a stream for reading.
int a;
int b;
line1stream >> a >> b; // reads "4 4" from 'line1' into a (now 4) b (now 4)
if (!stream || !line1stream)
{
// failed reading the first line.
// either reading the line failed (!stream)
// or reading 2 integers from line1stream failed (!line1stream)
throw SomeException("Failed");
}
std::string line2;
std::getline(stream, line2); // reads "3 1 2 1 b" into line2
std::stringstream line2stream(line2); // convers line2 into a stream for reading.
int data[4];
char c;
line2stream >> data[0] >> data[1] >> data[2] >> data[3] >> c;
if (!stream || !line2stream)
{
// failed reading the second line.
// either reading the line failed (!stream)
// or reading 4 integers and one char from line2stream failed (!line2stream)
throw SomeException("Failed");
}
ifstreams are also istreams, so you can use the same operator >> as with std::cin.
int main()
{
std::ifstream s("test.txt");
if (s.is_open())
{
int i, j, k;
s >> i >> j >> k;
}
}
Note that this is way not the fastest way of parsing, but that is probably irrelevant to you.

Using C++ ifstream extraction operator>> to read formatted data from a file

As my learning, I am trying to use c++ ifstream and its operator>> to read data from a text file using code below. The text file outdummy.txt has following contents:
just dummy
Hello ofstream
555
My questions is how to read char data present in the file into a char array or string. How to do this using the ifstream::operator>> in code below.
#include <iostream>
#include <fstream>
int main()
{
int a;
string s;
char buf[100];
ifstream in("outdummy.txt",ios_base::in);
in.operator>>(a); //How to read integer? How to read the string data.??
cout << a;
in.close();
getchar();
return 0;
}
If you want to use formatted input, you have to know in advance what data to expect and read it into variables of the according data type. For example, if you know that the number is always the fifth token, as in your example, you could do this:
std::string s1, s2, s3, s4;
int n;
std::ifstream in("outdummy.txt");
if (in >> s1 >> s2 >> s3 >> s4 >> n)
{
std::cout << "We read the number " << n << std::endl;
}
On the other hand, if you know that the number is always on the third line, by itself:
std::string line;
std::getline(in, line); // have line 1
std::getline(in, line); // have line 2
std::getline(in, line); // have line 3
std::istringstream iss(line);
if (iss >> n)
{
std::cout << "We read the number " << n << std::endl;
}
As you can see, to read a token as a string, you just stream it into a std::string. It's important to remember that the formatted input operator works token by token, and tokens are separated by whitespace (spaces, tabs, newlines). The usual fundamental choice to make is whether you process a file entirely in tokens (first version), or line by line (second version). For line-by-line processing, you use getline first to read one line into a string, and then use a string stream to tokenize the string.
A word about validation: You cannot know whether a formatted extraction will actually succeed, because that depends on the input data. Therefore, you should always check whether an input operation succeeded, and abort parsing if it doesn't, because in case of a failure your variables won't contain the correct data, but you have no way of knowing that later. So always say it like this:
if (in >> v) { /* ... */ } // v is some suitable variable
else { /* could not read into v */ }
if (std::getline(in, line)) { /* process line */ }
else { /* error, no line! */ }
The latter construction is usually used in a while loop, to read an entire file line by line:
while (std::getline(in, line)) { /* process line */ }
ifstream has ios_base::in by default. You don't need to specify it.
operator>> can be invoked directly as an operator: in >> a.
Reading strings is the same: in >> s, but the caveat is that it is whitespace-delimited, so it will read "just" by itself, without "dummy".
If you want to read complete lines, use std::getline(in, s).
Since you have elected to use C-strings, you can use the getline method of your ifstream object (not std::getline() which works with std::strings), which will allow you to specify the C-string and a maximum size for the buffer.
Based on what you had, and adding an additional buffer for the second line:
char buf[100];
char buf2[100];
in.getline(buf,sizeof(buf));
in.getline(buf2,sizeof(buf2));
in >> a;
However, as the other poster has proposed, try using the std::string and its methods, it will make your life easier.
You can read file contents and use a Finite State Machine for parsing.
Example:
void Parse(const char* buffer, size_t length);
size_t GetBufferSize();
size_t bufferSize = GetBufferSize();
char* buffer = new char[bufferSize];
std::ifstream in("input.txt");
while(in.getline(buffer, bufferSize)) {
Parse(buffer, in.gcount());
}
Alternatively, you can use a tool like Flex to write your parser.