0
votes

Kind of a basic question but I'm having troubles thinking of a solution so I need a push in the right direction.

I have an input file that I'm pulling in, and I have to put it into one string variable. The problem is I need to split this string up into different things. There will be 3 strings and 1 int. They are separated by a ":".

I know I can find the position of the first ":" by find(), but I really don't know how to progress through the string, for each thing and put it into it's own string / int.

The actual input from the file looks something like this:

A:PEP:909:Inventory Item

A is going to be command I have to execute... so that will be a string. PEP is a key, needs to be a string. 909 is an int.

and the last is a string.

So what I think I want to do is have 3 string var's, and 1 int and get all those things put into their respective variables.

So I think I'll end up wanting to conver this C++ string to a C string so I can use atoi to convert the one section to an int.

5
Asked and answered multiple times: stackoverflow.com/questions/53849/…Martin York
that's not quite the same. in this case, he needs to convert something out of it. a simply getline loop won't do itJohannes Schaub - litb
Check again at Martin's link. I posted my code over there. It's like a 3-line while loop to split strings. Then he can just use atol(stringVector[2].c_str()) to pull out the number. Or atoi() if he wants. Or atoll(). Or atoq().Mr.Ree

5 Answers

6
votes

I usually use something like this:

void 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);
    }
}

you can use it like this:

std::vector<std::string> tokens;
split("this:is:a:test", ':', tokens);

tokens will now contain "this", "is", "a", and "test"

3
votes

This is best done using std::getline and std::istringstream when you want to use the C++ Standard Library:

std::string command;
std::string key;
int         id;
std::string item;

std::string line = "A:PEP:909:Inventory Item";

// for each line: 
std::istringstream stream(line);

std::getline(stream, command, ':');
std::getline(stream, key, ':');
stream >> id;
std::getline(stream, item);

// now, process them

Consider putting it into an own struct:

struct record {
    std::string command;
    std::string key;
    int         id;
    std::string item;

    record(std::string const& line) {
        std::istringstream stream(line);
        stream >> *this;
    }

    friend std::istream& operator>>(std::istream& is, record & r){
        std::getline(is, r.command, ':');
        std::getline(is, r.key, ':');
        stream        >> r.id;
        std::getline(is, r.item);
        return is;
    }
};
3
votes
1
votes

With C-style strings you can use strtok() to do this. You could also use sscanf()

But since you're dealing with C++, you probably want to stick with built in std::string functions. As such you can use find(). Find has a form which takes a second argument which is the offset to start searching. So you can do find( ':' ) to find the first instance, and then use find( ':', firstIndex+1 ) to find the next instances, where firstIndex is the value returned by the first call to find().

1
votes

A comfortable solution that I found is not uncommon is the following prototype:

string SplitToken(string & body, char separator)

which returnsd everything up to the first occurence of separator, and removes that part including the separator.

"My" MFC - CString-based implementation looks as follows:

CString SplitStringAt(CString & s, int idx)
{
   CString ret;
   if (idx < 0)
   {
      ret = s;
      s.Empty();
   }
   else
   {
      ret = s.Left(idx);
      s = s.Mid(idx+1);
   }
   return ret;
}

CString SplitToken(CString & s,TCHAR separator)
{
   return SplitStringAt(s, s.Find(separator));
}

This is definitely not the most efficient method - the major drawback is that body is modified and a new (partial) copy is made for every token, so don't use it in performance-critical location!

However, I found this (and a few related functions) immensely useful, for simple parsers.