0

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.

3
  • Asked and answered multiple times: stackoverflow.com/questions/53849/… Commented Nov 28, 2008 at 1:55
  • that's not quite the same. in this case, he needs to convert something out of it. a simply getline loop won't do it Commented Nov 28, 2008 at 2:15
  • 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(). Commented Nov 28, 2008 at 3:01

5 Answers 5

6

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"

Sign up to request clarification or add additional context in comments.

3 Comments

simple and don't need to add another library. Perfect for me.
+1 my preferred solution, no messing with find() and indices.
gotta love the downvote on a year old post that works just fine...
3

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

5 Comments

Watch out that you don't wrap over into the next line when you hit a malformed entry with >3 or <3 colons.
yeah, it assumes the lines are correct. theoretically the key could also contain '\n', which would then span to the next line.
or you just first extract the whole line, and then use the ctor of record. this will not span into next lines indeed.
Just a comment, operator>> does not need to be declared friend. There are no private members, so it could be an external free function.
dribeas, it is already a free function. but it cannot be called directly and only works for koenig lookup (ADT) (see "friend name injection"). i like to use friend definition here, as it scales way with when you make the members private.
3

Take a look at boost::tokenizer.

1 Comment

1

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().

Comments

1

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.