views:

118

answers:

5

I'm pretty new to C++ and was looking for a good way to pull the data out of this line.

A sample line that I might need to tokenise is

f 11/65/11 16/70/16 17/69/17

I have a tokenisation method that splits strings into a vector as delimited by a string which may be useful

static void Tokenise(const string& str, vector<string>& tokens, const string& delimiters = " ")

The only way I can think of doing it is to tokenise with " " as a delimiter, remove the first item from the resulting vector, then tokenise each part by itself. Is there a good way to do this all in one?

+1  A: 

Judging from the sample line you can use two delimiters ' ' and '/' and you will get all your numbers.

static void Tokenise(const string& str, vector<string>& tokens, const string& delimiters = " /")

skwllsp
Thanks, but my tokenise function doesn't work that way. I'll be more clear in future :)
Ben
+3  A: 

I see the question is tagged as C++ but the absolutely easiest way to do this is with scanf

int indices[3][3];
sscanf(buffer, "f %d/%d/%d %d/%d/%d %d/%d/%d", &indices[0][0], &indices[0][1],...);
Andreas Brinck
That's quite a nice way of doing it also. I'll definitely keep in mind C functions in future, may even implement this. Easily the most tidy solution, which is nice.
Ben
For .obj parsing this seems to be, for me at least, the solution that is easiest to read and write. You'll end up with around ~50 lines of code to parse the entire file format. Don't go the tokenizer-lexer-parser route; it's way to convoluted for something as simple as this.
Jasper Bekkers
+1  A: 

You should take a look at Boost.Tokenizer and especially this:

// char_sep_example_1.cpp
#include <iostream>
#include <boost/tokenizer.hpp>
#include <string>

int main()
{
  std::string str = ";;Hello|world||-foo--bar;yow;baz|";
  typedef boost::tokenizer<boost::char_separator<char> > 
    tokenizer;
  boost::char_separator<char> sep("-;|");
  tokenizer tokens(str, sep);
  for (tokenizer::iterator tok_iter = tokens.begin();
       tok_iter != tokens.end(); ++tok_iter)
    std::cout << "<" << *tok_iter << "> ";
  std::cout << "\n";
  return EXIT_SUCCESS;
}
the_drow
Thanks. I'll check with my tutor if I can use Boost as part of this assignment.
Ben
Why the downvote?It fits...
the_drow
+1  A: 
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

class parse_error : public std::exception {};

template< typename Target >
inline Target convert_to(const std::string& value)
{
  std::istringstream iss(value);
  Target target;
  iss >> target >> std::ws;
  if(!iss || !iss.eof()) throw parse_error();
  return target;
}

template< typename T >
inline T read_delimited_value(std::istream& is, char delim)
{
  std::string value;
  std::getline(is,value,delim);
  if(!is) throw parse_error();
  return convert_to<T>(value);
}

template< typename It >
inline void output(std::ostream& os, It begin, It end)
{
  while(begin!=end)
    os << *begin++ << ' ';
}

int main()
{
  std::vector<int> values;
  const std::string line = "f 11/65/11 16/70/16 17/69/17";

  std::istringstream iss(line);
  std::string value;

  std::getline(iss,value,' ');
  if(value!="f" || !iss) throw parse_error();

  while(iss.good()) {
    values.push_back( read_delimited_value<int>(iss,'/') );
    values.push_back( read_delimited_value<int>(iss,'/') );
    values.push_back( read_delimited_value<int>(iss,' ') );
  }

  if(!iss.eof()) throw parse_error();

  output( std::cout, values.begin(), values.end() );
  std::cout << '\n';

  return 0;
}
sbi
Would whoever down-voted this please tell me what's wrong with it?
sbi
Upvoted one back for using templated interators!
Xavier Ho
I'm a bit fresh to programming to follow this well. However I will be reading up on generic programming and templating to figure this out. I'm under the impression this is a "correct" way to do it, however long it seems to be.Thanks :)
Ben
@Ben: This only seems long until you have those three functions in your toolbox. Once you have, what remains is what's in `main()`, and that isn't all that long.
sbi
Thanks. I'll probably be implementing this method, however I selected the other one simply because it answered my question more accurately
Ben
A: 

You can remove easily the first part until the first blank or the just after the f ( you can get the rest after the first blank with

istringstream iss( line );
std::getline( iss, restStr ,' ' )

Then you can use your tokenize function first on blank space and then on '/', or just use a set of std::getline and istringstreams in one loop.

int main()
{
    std::string s = "f 1/2/3 4/4/2";

    std::istringstream issLine( s );

    std::string result;

    // remove the first "f"
    std::getline( issLine, result, ' ' );

    // parse blanks
    while( std::getline( issLine, result, ' ' ) )
    {
        std::istringstream issToken( result );
        std::string token;

        //parse content
        while( std::getline( issToken, token, '/' ))
        {
            std::cout << token << ',';
            // add your data in whatever you want
        }
        std::cout << std::endl;
    }
}
Nikko