24

How can I split a string such as "102:330:3133:76531:451:000:12:44412 by the ":" character, and put all of the numbers into an int array (number sequence will always be 8 elements long)? Preferably without using an external library such as boost.

Also, I'm wondering how I can remove unneeded characters from the string before it's processed such as "$" and "#"?

phuclv
  • 37,963
  • 15
  • 156
  • 475
user2705775
  • 461
  • 1
  • 7
  • 14

9 Answers9

20

stringstream can do all these.

  1. Split a string and store into int array:

    string str = "102:330:3133:76531:451:000:12:44412";
    std::replace(str.begin(), str.end(), ':', ' ');  // replace ':' by ' '
    
    vector<int> array;
    stringstream ss(str);
    int temp;
    while (ss >> temp)
        array.push_back(temp); // done! now array={102,330,3133,76531,451,000,12,44412}
    
  2. Remove unneeded characters from the string before it's processed such as $ and #: just as the way handling : in the above.

PS: The above solution works only for strings that don't contain spaces. To handle strings with spaces, please refer to here based on std::string::find() and std::string::substr().

herohuyongtao
  • 49,413
  • 29
  • 133
  • 174
  • When I try your code I get these errors: (no operator "==" matches these operands operand types are: char == const char [2]) (a value of type "const char *" cannot be assigned to an entity of type "char") – user2705775 Dec 24 '13 at 07:54
  • instead of manually replacing the colons, you can just use [`std::replace(str.begin(), str.end(), ':', ' ');`](http://en.cppreference.com/w/cpp/algorithm/replace) – phuclv Jun 13 '17 at 03:47
  • 5
    this is a very specific answer that does not work if the strings contain spaces. – Martin Massera Jul 03 '17 at 23:00
  • @MartinMassera Thanks for pointing this out. Update the answer to intergrate this. Thanks. – herohuyongtao Dec 28 '19 at 05:19
11

The standard way in C is to use strtok like others have answered. However strtok is not C++-like and also unsafe. The standard way in C++ is to use std::istringstream

std::istringstream iss(str);
char c; // dummy character for the colon
int a[8];
iss >> a[0];
for (int i = 1; i < 8; i++)
    iss >> c >> a[i];

In case the input always has a fixed number of tokens like that, sscanf may be another simple solution

std::sscanf(str, "%d:%d:%d:%d:%d:%d:%d:%d", &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8);
phuclv
  • 37,963
  • 15
  • 156
  • 475
8

I had to write some code like this before and found a question on Stack Overflow for splitting a string by delimiter. Here's the original question: link.

You could use this with std::stoi for building the vector.

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

// use with:
const std::string numbers("102:330:3133:76531:451:000:12:44412");
std::vector<int> numbers = split(numbers, ':');

Here is a working ideone sample.

Community
  • 1
  • 1
Lander
  • 3,369
  • 2
  • 37
  • 53
  • Is there an easy way to store it in an array instead of a vector? – user2705775 Dec 24 '13 at 05:49
  • @user2705775 `std::copy(vector.begin(), vector.end(), std::begin(array))`. –  Dec 24 '13 at 05:58
  • 1
    @user2705775 the problem with storing it in an array is that you don't appear to know the number of characters before fully parsing the string. You can go with what remyabel if you really need it, but I don't quite see the point. – Lander Dec 24 '13 at 06:05
1

True ! there's no elven magic

Its kinda answered here too

#include <cstring>
#include <iostream>
#include<cstdlib>
#include<vector>

int main() 
{
    char input[100] = "102:330:3133:76531:451:000:12:44412";
    char *token = std::strtok(input, ":");
    std::vector<int> v;

    while (token != NULL) {
        v.push_back( std::strtol( token, NULL, 10 ));
        token = std::strtok(NULL, ":");
    }

    for(std::size_t i =0 ; i < v.size() ; ++i)
        std::cout << v[i] <<std::endl;
}

Demo Here

Community
  • 1
  • 1
P0W
  • 46,614
  • 9
  • 72
  • 119
1

To remove characters '#' and '$' you can use standard algorithm std::remove_if. However take into account that if there is for example the following string "12#34" then after removing '#' you will ge "1234". If you need that the resulted string will look as "12 34" or "12:34" then instead of std::remove_if it is better to use std::replace_if.

Below there is a sample code that performs the task. You need to include headers

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>

int main()
{
    char s[] = "102:$$330:#3133:76531:451:000:$12:44412";

    std::cout << s << std::endl;

    char *p = std::remove_if( s, s + std::strlen( s ), 
        []( char c ) { return ( c == '$' || c == '#' ); } );
    *p = '\0';

    std::cout << s << std::endl;

    const size_t N = 8;
    int a[N];

    p = s;
    for ( size_t i = 0; i < N; i++ )
    {
        a[i] = strtol( p, &p, 10 );
        if ( *p == ':' ) ++p;
    }

    for ( int x : a ) std::cout << x << ' ';
    std::cout << std::endl;
}

The output is

102:$$330:#3133:76531:451:000:$12:44412
102:330:3133:76531:451:000:12:44412
102 330 3133 76531 451 0 12 44412
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
0

Here's one way... not the cleverest but fast to write (8 repetition's verging on warranting a loop though). This approach to parsing is quite widely useful so good to learn. !(iss >> c) ensures there's no trailing non-whitespace characters in the string.

std::istringstream iss(the_string);
char c;
int n[8];
if (iss >> n[0] >> c && c == ':' &&
    iss >> n[1] >> c && c == ':' &&
    iss >> n[2] >> c && c == ':' &&
    iss >> n[3] >> c && c == ':' &&
    iss >> n[4] >> c && c == ':' &&
    iss >> n[5] >> c && c == ':' &&
    iss >> n[6] >> c && c == ':' &&
    iss >> n[7] && !(iss >> c))
    ...
Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
  • 1
    Why even bother checking `c == ':'`? – Beta Dec 24 '13 at 05:17
  • @Beta: it's generally considered a reasonable practice to check input looks as expected... if the colons are missing it might mean there's something else bogus about the input. For example, if someone passed "3.14 12.78 999.1 38.6" it would parse as `[ 3 14 2 78 99 1 8 6 ]` otherwise. (I get really tired of seeing S.O. questions where I/O problems would have been found if people checking input and output properly). – Tony Delroy Dec 24 '13 at 05:23
  • I'm getting an error "no operator ">>" matches these operands. operand types are: std::istringstream >> int" – user2705775 Dec 24 '13 at 05:51
  • @user2705775: did you `#include `? – Tony Delroy Dec 24 '13 at 06:04
0

You can use strtok() for split your string, perhaps in while loop.

When you get individual string then can use atoi(xxx) for conversion in ints.

Digital_Reality
  • 4,488
  • 1
  • 29
  • 31
0
#include <stdio.h>
#include <string.h>

int main ()
{
  char str[] ="102:330:3133:76531:451:000:12:44412";
  char * pch;
  printf ("Splitting string \"%s\" into tokens:\n",str);
  pch = strtok (str,":");
  while (pch != NULL)
  {
    printf ("%s\n",pch);
    pch = strtok (NULL, ":");
  }
  return 0;
}
phuclv
  • 37,963
  • 15
  • 156
  • 475
Imtiaz Emu
  • 479
  • 4
  • 5
0

Another solution using the regular expression features in C++11.

#include <algorithm>
#include <iostream>
#include <iterator>
#include <ostream>
#include <regex>
#include <sstream>
#include <string>
#include <vector>

int main()
{
    const std::string s = "102:330:3133:76531:451:000:12:44412";

    // Replace each colon with a single space
    const std::regex pattern(":");
    const std::string r = std::regex_replace(s, pattern, " ");

    std::istringstream ist(r);

    std::vector<int> numbers;
    std::copy(std::istream_iterator<int>(ist), std::istream_iterator<int>(),
        std::back_inserter(numbers));

    // We now have a vector of numbers

    // Print it out
    for (auto n : numbers)
    {
        std::cout << n << " ";
    }
    std::cout << std::endl;

    return 0;
}
user515430
  • 3,341
  • 2
  • 17
  • 13