0

I am trying to write a program that reads some numerical values from a .csv file, stores them in a std::vector<std::string>, and then converts these values into doubles and stores them in a std::vector<double>.

I am trying to do the conversion using stringstreams, which has worked fine for me in the past.

I have managed to import the numerical values and store them in the std::vector<std::string>, however I am getting a weird problem when trying to do the conversions to double. Only the very first value is stored in the std::vector<double> with a lot of significant figures missing, and the other entries are just ignored and not stored in the std::vector<double> at all.

Here is my code so far:

#include <string>
#include <fstream>
#include <iostream>
#include <sstream> 
#include <vector>

double extract_double(std::string str) 
{ 
    std::stringstream ss;
    double grade;
    //Pass all the course details to string stream
    ss << str; 
    //Extract the double type part of the string stream which is the course grade
    ss >> grade;
    str = "";
    ss.ignore();
    return grade;
}

int main()
{
    std::ifstream my_input_file;
    std::string file_name;
    my_input_file.open("scale_free_gamma_2_fitnesses.csv");
    int number_of_data_in_file;
    std::vector<std::string> fitnesses_string;
    std::vector<double> fitnesses;
    std::string string_temp;
    while (my_input_file.good()) {
        //Extract strings from file
        std::getline(my_input_file, string_temp, ',');
        fitnesses_string.push_back(string_temp);     
    }
    
    for (auto fitnesses_it = fitnesses_string.begin(); 
      fitnesses_it < fitnesses_string.end(); ++fitnesses_it){
        fitnesses.push_back(extract_double(*fitnesses_it));
    }

    for (auto fitnesses_itt = fitnesses.begin(); 
      fitnesses_itt < fitnesses.end(); ++fitnesses_itt){
        std::cout << *fitnesses_itt << std::endl;
    }
    return 0;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Your input loop structure is wrong. Read about why [here](https://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-i-e-while-stream-eof-cons). – molbdnilo Aug 12 '21 at 13:34
  • Can you provide first few lines from the input file? – Yksisarvinen Aug 12 '21 at 13:36
  • 2
    Side note: CSV files *usually* don't have a comma at the end of each line, so `std::getline(my_input_file,string_temp,',')` might merge the first element in the next row when you try to read the last element in a row, so do keep count of that if the CSV file that you input to your program does not have commas at the end of each line. – Ruks Aug 12 '21 at 13:37
  • A likely scenario is that your input file does not have the format you expect, causing the first conversion to return an uninitialized `grade` (it's never too soon to start initializing your variables and checking for errors), and the stream to enter an error state. – molbdnilo Aug 12 '21 at 13:39
  • 1
    A [CSV file](https://en.wikipedia.org/wiki/Comma-separated_values) is format where each line in a file represents a record and each field in the record is separated by a comma. Thus, a record ends with a new-line character and not a comma. Your code will not read a CSV file. Can you include an example of the input your are trying to read. – Daniel Dearlove Aug 12 '21 at 14:17
  • While having the code is nice, you haven't supplied enough information for anyone to help you. Read [ask] and [mre]. – sweenish Aug 12 '21 at 15:07

2 Answers2

0

A few things. First, there's a far better way to loop over your vectors.

for (const std::string &str: fitnesses_string) {
}

These are referred to as range-based for-loops, and I think you can see how much cleaner this syntax is. In modern C++, you rarely need to use iterators anymore. (I won't say never, but rarely.)

Next, your inner loop makes no sense at all. Instead, just do it quite simply:

for (const std::string &str: fitnesses_string) {
    fitnesses.push_back(extract_double(str));
}

Now, let's talk about how to convert a string to double.

fitnesses.push_back(std::stod(str));

So you don't need your method. There's already a method waiting for you. It's defined in #include <string>.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Joseph Larson
  • 8,530
  • 1
  • 19
  • 36
0

You should be reading individual lines from the file first, and then splitting up each line on commas.

And there are easier ways to handle the rest of your code, too.

Try something more like this instead:

#include <string>
#include <fstream>
#include <iostream>
#include <sstream> 
#include <vector>

int main()
{
    std::ifstream my_input_file("scale_free_gamma_2_fitnesses.csv");
    std::vector<std::string> fitnesses_string;
    std::vector<double> fitnesses;
    std::string line, string_temp;

    while (std::getline(my_input_file, line)) {
        //Extract strings from line
        std:::istringstream iss(line);
        while (std::getline(iss, string_temp, ','))
            fitnesses_string.push_back(string_temp);
    }
    
    for (const auto &s : fitnesses_string){
        fitnesses.push_back(std:stod(s));
    }

    for (auto value : fitnesses){
        std::cout << value << std::endl;
    }

    return 0;
}

In which case, even simpler would be to just get rid of the std::vector<std::string> altogether:

#include <string>
#include <fstream>
#include <iostream>
#include <sstream> 
#include <vector>

int main()
{
    std::ifstream my_input_file("scale_free_gamma_2_fitnesses.csv");
    std::vector<double> fitnesses;
    std::string line, string_temp;

    while (std::getline(my_input_file, line)) {
        //Extract strings from line
        std:::istringstream iss(line);
        while (std::getline(iss, string_temp, ','))
            fitnesses.push_back(std:stod(string_temp));
    }
    
    for (auto value : fitnesses){
        std::cout << value << std::endl;
    }

    return 0;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770