I am trying to read an unknown number of double values stored on separate lines from a text file into a vector called rainfall. My code won't compile; I am getting the error no match for 'operator>>' in 'inputFile >> rainfall' for the while loop line. I understand how to read in from a file into an array, but we are required to use vectors for this project and I'm not getting it. I appreciate any tips you can give on my partial code below.
vector<double> rainfall; // a vector to hold rainfall data
// open file
ifstream inputFile("/home/shared/data4.txt");
// test file open
if (inputFile) {
int count = 0; // count number of items in the file
// read the elements in the file into a vector
while ( inputFile >> rainfall ) {
rainfall.push_back(count);
++count;
}
// close the file
I think you should store it in a variable of type double. Seems you are doing >> to a vector, which is not valid. Consider the following code:
// open file
ifstream inputFile("/home/shared/data4.txt");
// test file open
if (inputFile) {
double value;
// read the elements in the file into a vector
while ( inputFile >> value ) {
rainfall.push_back(value);
}
// close the file
As #legends2k points out, you don't need to use the variable count. Use rainfall.size() to retrieve the number of items in the vector.
You cannot use >> operator to read in the whole vector. You need to read one item at a time, and push it into the vector:
double v;
while (inputFile >> v) {
rainfall.push_back(v);
}
You do not need to count the entries, because rainfall.size() will give you the exact count.
Finally, the most C++ -ish way of reading a vector is with istream iterators:
// Prepare a pair of iterators to read the data from cin
std::istream_iterator<double> eos;
std::istream_iterator<double> iit(inputFile);
// No loop is necessary, because you can use copy()
std::copy(iit, eos, std::back_inserter(rainfall));
You could also do:
#include <algorithm>
#include <iterator>
...
std::istream_iterator<double> input(inputFile);
std::copy(input, std::istream_iterator<double>(),
std::back_inserter(rainfall));
...
assuming you like the STL.
The input operator >> is not defined for inputting doubles into a std::vector.
Instead construct the std::vector with two tokenizing input iterators for the input file.
Here's an example of how you can do it by using only 2 lines of code:
std::ifstream inputFile{"/home/shared/data4.txt"};
std::vector<double> rainfall{std::istream_iterator<double>{inputFile}, {}};
Another solution is to define the input operator function as:
std::istream& operator>> (std::istream& in, std::vector<double>& v) {
double d;
while (in >> d) {
v.push_back(d);
}
return in;
}
Then you can use it as in your example:
std::vector<double> rainfall;
inputFile >> rainfall;
Consider using this input reader:
#include <iterator>
#include <algorithm>
#include <vector>
#include <iostream>
#include <fstream>
template<typename T>
std::vector<T> parse_stream(std::istream &stream) {
std::vector<T> v;
std::istream_iterator<T> input(stream);
std::copy(input, std::istream_iterator<T>(), std::back_inserter(v));
return v;
}
int main(int argc, char* argv[])
{
std::ifstream input("/home/shared/data4.txt");
std::vector<int> v = parse_stream<int>(input);
for(auto &item: v) {
std::cout << item << std::endl;
}
return 0;
}
It allows you to use other stream types as well and is generic over type read.
Related
I write a code in which I want to pass several strings from text file to string vector. Currently I do this that way:
using namespace std;
int main()
{
string list_name="LIST";
ifstream REF;
REF.open(list_name.c_str());
vector<string> titles;
for(auto i=0;;i++)
{
REF>>list_name;
if(list_name=="-1"){break;}
titles.push_back(list_name);
}
REF.close();
cout<<titles.size();
for(unsigned int i=0; i<titles.size(); i++)
{
cout<<endl<<titles[i];
}
It works fine, I get the output as expected. My concern is is there more elegant way to pass string from text file to vector directly, avoiding this fragment, when passing string from filestream to string object and assigning it to the vector with push_back as separate step:
REF>>list_name;
if(list_name=="-1"){break;}
titles.push_back(list_name);
More elegant way with algorithms
std::copy_if(std::istream_iterator<std::string>(REF),
std::istream_iterator<std::string>(),
std::back_inserter(titles),
[](const std::string& t) { return t != "-1"; });
The other answers are maybe too complicated or too complex.
Let me first do a small review of your code. Please see my comments within the code:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std; // You should not open the full std namespace. Better to use full qualifiacation
int main()
{
string list_name = "LIST";
ifstream REF; // Here you coud directly use the construct ofr the istream, which will open the file for you
REF.open(list_name.c_str()); // No need to use c_str
vector<string> titles; // All variables should be initialized. Use {}
for (auto i = 0;; i++) // Endless loop. You could also write for(;;), but bad design
{
REF >> list_name;
if (list_name == "-1") { break; } // Break out of the endless loop. Bad design. Curly braces not needed
titles.push_back(list_name);
}
REF.close(); // No nbeed to close the file. With RAII, the destructor of the istream will close the file for you
cout << titles.size();
for (unsigned int i = 0; i < titles.size(); i++) // Better to use a range based for loop
{
cout << endl << titles[i]; // end not recommended. For cout`'\n' is beter, because it does not call flush unneccesarily.
}
}
You see many points for improvement.
Let me explain some of the more important topics to you.
You should use the std::ifstreams constructor to directly open the file.
Always check the result of such an operation. The bool and ! operator for the std::ifstream are overwritten. So a simple test can be done
Not need to close the file. The Destructor of the std::ifstream will do that for you.
There is a standard approach on how to read a file. Please see below.
If you want to read file until EOF (end of file) or any other condition, you can simply use a while loop and call the extraction operator >>
For example:
while (REF >> list_name) {
titles.push_back(list_name);
}
Why does this work? The extraction operator will always return a reference to the stream with what it was called. So, you can imagine that after reading the string, the while would contain while (REF), because REF was returned by (REF >> list_name. And, as mentioned already, the bool operator of the stream is overwritten and returns the state of the stream. If there would be any error or EOF, then if (REF) would be false.
So and now the additional condition: A comparison with "-1" can be easily added to the while statement.
while ((REF >> list_name) and (list_name != "-1")) {
titles.push_back(list_name);
}
This is a safe operatrion, because of boolean short-cut evaluation. If the first condition is already false, the second will not be evaluated.
With all the knwo-how above, the code could be refactored to:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
int main() {
// Here our source data is stored
const std::string fileName{ "list.txt" };
// Open the file and check, if it could be opened
std::ifstream fileStream{ fileName };
if (fileStream) {
// Here we will store all titles that we read from the file
std::vector<std::string> titles{};
// Now read all data and store vit in our resulting vector
std::string tempTitle{};
while ((fileStream >> tempTitle) and (tempTitle != "-1"))
titles.push_back(tempTitle);
// For debug purposes. Show all titles on screen:
for (const std::string title : titles)
std::cout << '\n' << title;
}
else std::cerr << "\n*** Error: Could not open file '" << fileName << "'\n";
}
If you knew the number of strings to read beforehand, you could
using StringVector = std::vector<std::string>;
int main(int argc, const char* argv) {
constexpr size_t N = 4; // or however many strings you want...
StringVector data(N);
std::ifstream stream("foo.txt");
for (size_t i =0; (i < N) && stream; i++) {
stream >> data[i];
}
}
But this would be less flexible and it would be trickier to implement your "-1" "terminator" convention.
If that "-1" thing is a true requirement (in contrast to an arbitrary choice), and if you use this more than once, it might pay off to "abstract", how you read those strings. Abstraction is usually done in form of a function.
// compile with:
// clang++-13 -std=c++20 -g -O3 -o words words.cpp
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
using StringVector = std::vector<std::string>;
std::istream& operator>> (std::istream& stream, StringVector& sv)
{
std::string word;
while (stream) {
stream >> word;
if (word == "-1")
return stream;
sv.push_back(word);
}
return stream;
}
std::ostream& operator<< (std::ostream& stream,
const StringVector& sv) {
for (const auto& s : sv) {
stream << s << std::endl;
}
return stream;
}
int main(int argc, const char* argv[]) {
std::string file_data{R"(word1 word2
word3
word4 -1)"};
std::istringstream stream(file_data);
StringVector data;
data.reserve(10);
stream >> data;
std::cout
<< "Number of strings loaded: "
<< data.size() << std::endl;
std::cout << data;
return 0;
}
The above operator>>() works for streams in general, so it also works for file streams.
As an aside: One reason, why people would not like the "-1" terminator approach is performance. If you keep pushing into a vector an arbitrary amount of times, the storage of the vector needs to be re-allocated as the vector grows, which is avoidable overhead. So, usually people would use another file format, e.g. giving the number of strings first, then the strings, which would allow for:
size_t n;
stream >> n;
StringVector data;
data.reserve(n); // avoids "spurious reallocs as we load the strings"
for (size_t i = 0; i < n; i++) { ... }
I am trying to read this csv file, let's call it "file.csv", and I'm trying to put it into a vector of double.
This csv contains numbers like:
755673.8431514322,
684085.6737614165,
76023.8121728658,
...
I tried using stringstream, and it successfully input these number to the vector but the input numbers is not like I wanted. Instead, the inputted numbers are
7556373, 684085, 76023.8
How can I read the whole digits without throwing any of it away?
This is my code
vector<long double> mainVector;
int main()
{
ifstream data;
data.open("file.csv");
while (data.good())
{
string line;
stringstream s;
long double db;
getline(data, line, ',');
s << line;
s >> db;
mainVector.push_back(db);
}
}
How to read the whole digits without throwing any of it.
As #user4581301 mentioned in the comments, I guess you are missing std::setprecision() while outputting.
However, you do not need std::stringstream to do the job. Convert line(which is a string directly to double using std::stold and place into the vector directly as follows.
That being said, use of std::stold will make sure not to have wrong input to the vector, by throwing std::invalid_argument exception, if the conversion from string to double was unsuccessful. (Credits to #user4581301)
#include <iostream>
#include <fstream>
#include <vector> // std::vector
#include <string> // std:: stold
#include <iomanip> // std::setprecision
int main()
{
std::vector<long double> mainVector;
std::ifstream data("file.csv");
if(data.is_open())
{
std::string line;
while(std::getline(data, line, ','))
mainVector.emplace_back(std::stold(line));
}
for(const auto ele: mainVector)
std::cout << std::setprecision(16) << ele << std::endl;
// ^^^^^^^^^^^^^^^^^^^^
return 0;
}
I'm having a bit of a problem with extracting data from a simple .txt file with the getline command.
The txt file is very simple: a column of 400 numbers. I use a vector to store them with the following code:
int i = 0;
string line;
vector <double> vec;
while (getline(input, line))
{
vec.push_back(i);
N++;
input >> vec[i];
i++;
}
It correctly creates a vector of 400 elements but first line of txt file is ignored (I end up with vec[0] = 2nd line of txt file instead of 1st) and 399th element is 399 instead of the 400th line of txt file.
I tried several other ways to extract this data but it was unsuccessful.
Thank you for your help!
EDIT:
I have edited the code according to some of the remarks:
vector <double> vec;
string line;
double num;
while (getline(input, line))
{
input >> num;
vec.push_back(num);
}
Unfortunately, it still skips the first line of my text file.
EDIT 2 --> SOLUTION:
Thanks to all of your remarks, I realized that I was doing something wrong when using both getline and input >> num;
Here is how the problem was solved:
double num;
vector <double> vec;
while (input >> num)
{
vec.push_back(num);
}
You can read the entire file into a vector just by passing std::istream_iterator to std::vector constructor, without loops:
std::vector<int> v{
std::istream_iterator<int>{input},
std::istream_iterator<int>{}
};
E.g.:
#include <iostream>
#include <iterator>
#include <vector>
#include <exception>
template<class T>
std::vector<T> parse_words_into_vector(std::istream& s) {
std::vector<T> result{
std::istream_iterator<T>{s},
std::istream_iterator<T>{}
};
if(!s.eof())
throw std::runtime_error("Failed to parse the entire file.");
return result;
}
int main() {
auto v = parse_words_into_vector<int>(std::cin);
std::cout << v.size() << '\n';
}
You loose the first line due to reading from the file once more - here:
while (getline(input, line))
// ^^^^^^^ Here you read the first line
{
input >> num;
// ^^^^^^^^ Here you will read the second line
You told you want a vector of doubles - like:
std::vector<double> vec;
So you should use std::stod to convert the line read by getline into a double. Like:
while (std::getline(input, line))
{
// Convert the text line (i.e. string) to a floating point number (i.e. double)
double tmp;
try
{
tmp = stod(line);
}
catch(std::invalid_argument)
{
// Illegal input
break;
}
catch(std::out_of_range)
{
// Illegal input
break;
}
vec.push_back(tmp);
}
Don't do input >> num; inside the loop.
If you really want to use input >> num; then you shall not use getline. That is - you can use either but not both.
Change your while loop like below:-
while (getline(input, line))
{
vec.push_back(line);
//N++;
//input >> vec[i];
//i++;
}
Also try with below option
do{
vec.push_back(i);
//N++;
//i++;
}while (input >> vec[i++]);
You are starting by putting 0 in vector in first iteration:
vec.push_back(i);
then after you read first line you read next string, but stream get pointer from file is already in different place, so you override this 0 and skip first value from stream. What is worse that is oddly converted to double:
input >> vec[i];
This way you will get wrong.
Try this:
while (std::getline(file, line)) {
//In c++11
vec.emplace_back(std::stod(line));
//In c++ 98, #include <stdlib.h> needed
//vec.push_back(atof(line.c_str()));
}
This assumes you will always have proper file.
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...
}
I need to read some files containing a lot of numbers (int). Lines of every file are different.
1
3
5
2
1
3
2
I have to read one of those files and create an array of int dynamically.
I'm going to read the file twice because I'm not able to know the length of the file.
Do you know another way ?
This is what I did:
int main()
{
int *array;
int tmp, count;
ifstream fin("inputfile");
while(fin >> tmp)
count++;
array = new int[count];
fin.close();
fin.open("inputfile");
int i=0;
while(fin >> tmp)
array[i++]=tmp;
delete[] array;
return 0;
}
Thanks for your help.
Use a std::vector rather that a raw array.
That way you can add to the vector as you read each item, rather than having to read the file once in order to work out how many items are in the file and then again to populate the array.
int main()
{
std::vector<int> data;
int tmp;
ifstream fin("inputfile");
while(fin >> tmp)
{
data.push_back(tmp)
}
return 0;
}
Here is an idiomatic way of reading numbers from a file into an std::vector<int>:
#include <iostream>
#include <iterator>
#include <fstream>
#include <vector>
int main()
{
std::ifstream is("inputfile");
std::istream_iterator<int> start(is), end;
std::vector<int> numbers(start, end);
std::cout << "Read " << numbers.size() << " numbers" << std::endl;
}
if there are same numbers of int in a line for all files, you can get the count of line of the file by calculating the size of it, and then the lines of this file is equal to size(file)/(n*sizeof(int)), n is the number of int for every line. I think you can try instead of read the file twice.
If you don't want use std::vector, you can add a count for in the while loop, if it reaches the up limit of the array, realloc another buffer with size*2 and copy the data to it, then start read the file again.
vector use the same logic.