3

I am writing a program that reads data from a file.

the .txt file looks like this:

Text, numbers, : 5,3,5

Text, numbers, : 1,3,7,8

I was successful in extracting the "Text" and "Numbers", However when I came across the numbers after the : "5,3,5", I was really stuck.

I need to change these numbers to ints and add them to a vector of int, so I got rid of the commas, then tried using stoi to convert them to ints, however, stoi was only "extracting" the first number, in this case, only 5 and 1, this is what I've tried:

while(getline(file, line)){
   stringstream ss(line);
   getline(ss, text, ',');
   getline (ss, nums, ':');
   getline (ss, numList, ',' );
   replace(numList.begin(), numList.end(), ',' , ' ');
   vec.push_back(stoi(numList));

   randomStruct str = {text, nums, numList};

   randomStructVec.push_back(str);


}

I need the output to look like this when printing the elements of the vector:

5 3 5

1 3 7 8

what I'm getting is :

5

1

and other times I get duplicate numbers as well: 5

1111

or

5555

11

I need a way to make the stoi function convert all the numbers on that one line of string to ints, and store them in a vec of ints.

Any help, would be greatly appreciated.

Hugs
  • 65
  • 5
  • 3
    Have a look at this answer to [SO: reading from text file to struct vector, but text file lines are of different length](https://stackoverflow.com/a/57586936/7478597). It solves a similar problem. (Your issue is that `vec.push_back()` stores exactly one value. You have to put it into a loop.) – Scheff's Cat Sep 02 '19 at 08:18

3 Answers3

2

Check out my solution at How do I tokenize a string that uses the String Toolkit Library

Here is a paired down version for your case:

#include <iostream>
#include <vector>
#include <string>
#include <strtk.hpp>  //String Toolkit Library

const char *whitespace  = " \t\r\n\f";
const char *whitespace_and_punctuation  = " \t\r\n\f;,=";

int main()
{

  // parsing a string into a vector of integers with  separators
  // of spaces and punctuation

       std::string s("3; 4, 4, 8,-1");
       std::vector<int> values;
       if( strtk::parse( s, whitespace_and_punctuation, values ) )
       {
           for(size_t i = 0; i < values.size(); ++i )
            std::cout << values[i] << std::endl;
       }

    return 0;
}

You will notice the conversion of the values into a vector of integers. The library is header only. The library is VERY fast and can handle most anything you need to do with a string and parsing.

DannyK
  • 1,342
  • 16
  • 23
1

Use this function stoi_() in this function, I am converting a string into a number if that string contains the characters in range 0-9 otherwise create a new string and repeat this process until you reach the end of string. To handle negative numbers you have to add one more condition.

vector<int> stoi_(string s){
    vector<int>ans;
    int i = 0;
    int n =s.length();
    while(i<n){
        string temp = ""; // current number 

       if(s[i]=='-' && (i+1<n && (s[i+1]>='0' && s[i+1]<='9'))){ // handle -ve numbers
           temp = temp + s[i];
           i++;
       }
        while(i<n && s[i]>='0' && s[i]<='9'){ // if current character is number append it into cur number
            temp = temp + s[i];
            i++;
        }
        if(temp.length()>0){
            ans.push_back(stoi(temp)); // here using stoi() for simplicity 
        }
        else{
            i++;
        }
    }
    return ans;
}
shubham jha
  • 1,410
  • 12
  • 19
1

There is too main problem in your code.


First of all getline (ss, numList, ',' ); will stop on the first value of the list. In fact, when your list is 5,3,5, getline (ss, numList, ','); will read 5 then , so it will stop. At this point, numValue == "5"

This is quite simple to fix : Just remove the delimiter char, so getline(ss, numList);. Using this, numValue == "5,3,5"


Alright, now you have all your value. You replace ',' by ' ' in order to separate your numbers. Good, numList = "5 3 5".

And then is your second error : vec.push_back(stoi(numList));. stoi(numList) return an int and is not able to get through space characters. So it will only convert the first 5 and return it. You will never get the other numbers, as you don't even do a loop.

Here is my solution : convert your string to a stringstream and use >> operator

std::stringstream numStream(numList);
int value;
while(numList >> value)
    vec.push_back(value);

So we end up with your final code (I removed stds, as it seems that you wrote using namespace std somewhere in your code)

struct randomStruct
{
   string txt,
   string nb, 
   vector<int> data
}
// -------
while(getline(file, line)){
   stringstream ss(line);
   getline(ss, text, ',');
   getline (ss, nums, ':');
   getline (ss, numList);
   replace(numList.begin(), numList.end(), ',' , ' ');
   stringstream numStream(numList);
   int value;
   while(numStream >> value)
      vec.push_back(value);

   randomStruct str = {text, nums, vec};

   randomStructVec.push_back(str);
   vec.clear();
}

// Accessing and printing data
for (auto str : randomStructVec)
{
   for (auto i : str.data)
   {
      cout << i << " ";
   }
   cout << endl;
}
DrosvarG
  • 483
  • 3
  • 14
  • Hi Buddy, I'm getting this weird error while trying to compile: "Error: Using deleted function 'std::stringstream::basic_stringstream(const std::stringstream &)', on line auto numStream = stringstream(numList); Thanks for your reply anyway! I'm getting closer! – Hugs Sep 02 '19 at 12:53
  • it is indeed a string. – Hugs Sep 02 '19 at 13:11
  • 1
    @Hugs Probably caused by `auto` syntax, try removing the line and use `stringstream numStream(numList);` to initialize `numStream` instead – DrosvarG Sep 02 '19 at 13:16
  • Thank you, it works, but when I try to output the elements of the vector using for(auto i : str.numList){ cout << i << ""; } Output is weird and duplicated numbers : 1155115111, am I outputting this wrong? I really appreciate your help! – Hugs Sep 02 '19 at 13:28
  • 1
    Your formated data is stored in `vec` (which is of type `vector`). NumList is the result of `getline(ss, numList);` and should be `" 5 3 5"` for your first line and `" 1 3 7 8"` for your second line... I'm wondering : do you need to store the value in a `vector` or you just need to print it into `cout` ? – DrosvarG Sep 02 '19 at 13:33
  • I need to store it in a vector, because I'll have to use these numbers that correspond to each struct object (str) later on, so when I attempt to output the details of an str object, it would show these numbers with the other data, something like this: text, nums, numList. – Hugs Sep 02 '19 at 13:39
  • 1
    I edited my answer so you can have a look on what should be `randomStruct` and how you can print your data. Mind that I also changed `randomStruct str = {text, nums, vec};` – DrosvarG Sep 02 '19 at 13:56
  • Almost there, only problem now are the duplicates, on each line there are numbers of the previous one, example: 5 3 5(first line) 5 3 5 1 3 7 8 (sec line) etc... it seems like for some reason the previous line, is getting carried over to the next one. – Hugs Sep 02 '19 at 14:13
  • 1
    @Hugs Sure, I forgot to clear the vector at the end of each while iteration. Add `vec.clear();` after `randomStructVec.push_back(str);` (I'm editing this) – DrosvarG Sep 02 '19 at 14:36
  • Thank you! I knew it was something about clear! I have been working on this since 7 am, and it's almost 1 am now, Thanks a ton, I sincerely appreciate your help :D – Hugs Sep 02 '19 at 14:43