C ++ how to get ONLY integers from a complex string

I have multiple lines, each containing one word and multiple integers (one line is a whole line):

Adam 2 5 1 5 3 4
John 1 4 2 5 22 7
Kate 7 3 4 2 1 15
Bill 2222 2 22 11 111

      

As you can see, each word / number is separated by a space. Now I want to load this data into a map where the word (name) will be the key and the value will be a vector of numbers in the string. I already have the key values ​​in an isolated temporary stl container, so the challenge is to load only integers from each row into a 2D vector and then concatenate those two into a map. The question is, is there any C ++ function that will avoid words and whitespace and only get integers from a string, or do I need to look for char -by- char like strings here ?

I only found a partial solution that cannot get more than one digit:

vector<int> out;

for (int i = 0; i < line.length(); i++) {

    if (isdigit(line.at(i))) {  
        stringstream const_char;
        int intValue;
        const_char << line.at(i); 
        const_char >> intValue;
        out.push_back(intValue);
    }
}

      

+3


source to share


5 answers


If each line is in the format "number number number ..." then use a string stream and skip that word by reading it.

If the current line is in line

:



vector<int> out;
istringstream in(line);
string word;
in >> word;
int x = 0;
while (in >> x)
{
    out.push_back(x);
}

      

+2


source


split the line on spaces as that appears to be your delimiter. Then check that each substring contains an int with strol .

Then use stoi to convert integer substrings to int.



If no conversion of stoi can be performed (the string does not contain a number) an invalid_argument exception is thrown, so do not try to convert the substring of names.

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

std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
    std::stringstream ss(s);
    std::string item;
    while (std::getline(ss, item, delim)) {
        elems.push_back(item);
    }
    return elems;
}


std::vector<std::string> split(const std::string &s, char delim) {
    std::vector<std::string> elems;
    split(s, delim, elems);
    return elems;
}

inline bool isInteger(const std::string & s)
{
   if(s.empty() || ((!isdigit(s[0])) && (s[0] != '-') && (s[0] != '+'))) return false ;

   char * p ;
   strtol(s.c_str(), &p, 10) ;

   return (*p == 0) ;
}


int main()
{
   std::cout << "Hello World" << std::endl;

   std::string example="Adam 2 5 1 5 3 4";

   std::vector<std::string> subStrings;

   subStrings = split(example, ' ');

   std::string sItem;
   for(std::vector<std::string>::iterator it = subStrings.begin(); it != subStrings.end(); ++it) {
        sItem = *it;
        if( isInteger(sItem) ){
            int nItem = std::stoi (sItem);
            std::cout << nItem << '\n';
        }
    }

   return 0;
}

      

+2


source


use find () and substr () of the string class to find the name if it is always at the beginning of the string.

std::string s = "Adam 2 5 1 5 3 4";
std::string delimiter = " ";
s.substr(0, s.find(delimiter)); //To get the name
s.erase(0, s.find(delimiter)); //To delete the name
//Repeat the mechanism with a for or a while for the numbers

      

I am not testing this solution, but I use something like this, always with the label first.

If the name could be anywhere, I can't see how to check it without checking for every character.

+1


source


Assuming the name comes first, here is a function that will read the string and append to the map.

#include <map>
#include <vector>
#include <sstream>
#include <string>
#include <algorithm>

using namespace std;

typedef std::map<std::string, std::vector<int> > StringMap;

void AddToMap(StringMap& sMap, const std::string& line)
{
    // copy string to stream and get the name
    istringstream strm(line);
    string name;
    strm >> name;

    // iterate through the ints and populate the vector
    StringMap::iterator it = sMap.insert(make_pair(name, std::vector<int>())).first;
    int num;
    while (strm >> num)
        it->second.push_back(num);
}

      

The function above adds a new entry to the card with the first read and the subsequent read fills the vector.

Note that the function map::insert

returns std::pair

where first

this pair is an iterator to the generated map. So we just get an iterator and write push_back from there.

Here's a test program:

    int main()
    {
        vector<std::string> data = { "Adam 2 5 1 5 3 4", "John 1 4 2 5 22 7", 
                                     "Kate 7 3 4 2 1 15", "Bill 2222 2 22 11 111" };
        StringMap vectMap;

        // Add results to map
        for_each(data.begin(), data.end(), 
                 [&](const std::string& s){AddToMap(vectMap, s); });

        // Output the results
        for_each(vectMap.begin(), vectMap.end(),
                 [](const StringMap::value_type& vt)
                 {cout << vt.first << " "; copy(vt.second.begin(), vt.second.end(), 
                  ostream_iterator<int>(cout, " ")); cout << "\n"; });
    }

      

Live example: http://ideone.com/8UlnX2

+1


source


Here is a program that demonstrates an approach to a task that can be used.

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <sstream>
#include <iterator>

int main() 
{
    std::string s( "Adam 2 5 1 5 3 4" );
    std::map<std::string, std::vector<int>> m;
    std::string key;
    std::istringstream is( s );

    if ( is >> key )
    {
        m[key] = std::vector<int>( std::istream_iterator<int>( is ),
                                   std::istream_iterator<int>() );
    }

    for ( const auto &p : m ) 
    {
        std::cout << p.first << ": ";
        for ( int x : p.second ) std::cout << x << ' ';
        std::cout << std::endl;
    }

    return 0;
}

      

Output signal

Adam: 2 5 1 5 3 4

      

+1


source







All Articles