tags:

views:

249

answers:

7

Say you have

char *=  "name:454";

What is the best way to parse name and the number, thus

std:string id would equal to "name";

double d would equal to 454;

STL please, no boost.

+3  A: 

You want to look at the strtok function using ':' as the token. Here is an example:

#include <stdio.h>
#include <string.h>

int main ()
{
    char str[] = "name:454";
    char* c = strtok(str, ":");

    while (c != NULL)
    {
     printf("%s\n", c);
     c = strtok(NULL, ":");
    }

    return 0;
}
Andrew Hare
strtok is confusing but it is one way to tokenize a string. You may want to make sure strtok is thread safe. Historically, that was a problem, but I don't know nowadays.
Sam Hoice
That is a little bit too much C I think.
Lennart
He did say STL only - is there a better way without rolling custom code?
Andrew Hare
@Andrew: strtok() is one of the few gotchas of the C standard library. I wish we could move on. :sigh:
dirkgently
I agree that there are better solutions but I am only working within the parameters of the request (STL). I think a custom tokenizer function would be best here, personally.
Andrew Hare
if you're concerned about thread safety, you can use strtok_r. It does the same as strtok, but thread safe.
Nathan Fellman
+2  A: 

Not the stl solution you asked for; however, sooner or later you'll need to parse more complicated expressions (hint - sooner than you expect), at which point you will need regular expressions - so why not start now. Your expression is captures by:

^(\w+):(\d+)$

I'm no boost fanatic, but their regexp library is nice.

David Lehavi
tell my employer that :) we don't use boost here :(... stl please, can't accept this answer....thx
+1  A: 
#include <iostream>
#include <sstream>
#include <string>

int main() {
  /* output storage */
  std::string id; 
  double d;
  /* convert input to a STL string */
  std::string s("name:454");
  size_t off = std::string::npos;
  /* smart replace: parsing is easier with a space */    
  if ((off = s.find(':')) != std::string::npos) { // error check: all or none 
    s = s.replace(off, 1, 1, ' ');
    std::istringstream iss(s);
    iss >> id >> d;
    std::cout << "id = " << id << " ; d = " << d << '\n';
  }
  return 0;
}

Though, I'd just write my own parser or use the C scanner function for speed.

dirkgently
doesn't work. The result is s.replace(s.find(':'), 1, 1, ' '); istringstream iss("name:454"); iss >> id >> d; cout<< "ID="<<id<<", num="<<d<<endl; ID=name:454, num=4.85842e-270
corrected the error
The point of your edit? Why take out the error check? An assignment was missing. Reinstating in former glory. Let me know if this is what you wanted, though.
dirkgently
@dirkgently --exactly what I needed; just made it into production code :)
@Sasha: hang on. I'll make one last edit.
dirkgently
@dirkgently thanks man
@Sasha: Don't mention it :) Just be sure you have all the appropriate checks in your production code. Please!
dirkgently
A: 

I would write my own. The basic idea would be to read one character at a time from the stream. If it isn't a colon, append it to the id string. If it is, skip it, then use the istream >> operator to load an integer, double float, or whatever is needed. Then you probably put the result into a std::map.

Zan Lynx
A: 

Another possibility is to use an already written parser for a simple format like INI.

Here is one: http://code.jellycan.com/SimpleIni/

I looked at the code for SimpleIni and it isn't very C++ and STL'ish, but do you really care?

Zan Lynx
no way, I am using a library for such a rudimentary calculation :)
A: 
template < typename T >
T valueFromString( const std::string &src )
{
    std::stringstream s( src );
    T result = T();
    s >> result;
    return result;
}

std::string wordByNumber( const std::string &src, size_t n, const std::string &delims )
{
    std::string::size_type word_begin = 0;
    for ( size_t i = 0; i < n; ++i )
    {
        word_begin = src.find_first_of( delims, word_begin );
    }
    std::string::size_type word_end = src.find_first_of( delims, word_begin );

    word_begin = std::string::npos == word_begin || word_begin == src.length() ? 0 : word_begin + 1;
    word_end = std::string::npos == word_end ? src.length() : word_end;
    return src.substr( word_begin, word_end - word_begin);
}


char *a = "asdfsd:5.2";
std::cout << wordByNumber( a, 0, ":" ) << ", " << valueFromString< double > ( wordByNumber( a, 1, ":" ) );

PS: in previous revision I've posted wordByNumber - which skipped neighbored delimiters (e.g.:::), in current revision they are treated as empty word.

bb
An interesting solution!
But implementing and debug takes a more time than other solutions. Use it if needed=)
bb