Reputation: 20800
I'm writing simple OBJ loader and I encountered next problem - I have to extract integers from next std::string
:
f v0/vt0/vn0 v1/vt1/vn0 ... vk/vtk/vnk
where vk
, vtk
, vnk
are int values and there is no space between /
and the values and only one space between groups.
As the files can be quite large and this type of lines may appear more than 100000 times I need an efficient way to extract the integers from strings like this one.
EDIT:
As Jesse asked this is my current approach(I assume data is in the right format!):
int p, t, n;
const char* l = line.c_str() + 2;
for (int vIndex = 0; l && sscanf(l, "%d/%d/%d", &p, &t, &n) == 3; ++vIndex)
{
//do something with vertex
l = strchr(l, ' ');
if (l)
{
l += 1;
}
}
Upvotes: 4
Views: 960
Reputation: 3778
Use boost spirit, it is far more powerful and allow easy evolution.
http://www.boost.org/doc/libs/1_54_0/libs/spirit/doc/html/spirit/qi/tutorials/semantic_actions.html
Here is a example solving your problem by putting your vertices in a vector. If you want to call another function, see phoenix doc:
I admit Phoenix/Spirit has a high entry cost, but I think it is worth the pain.
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/home/phoenix/object/construct.hpp>
#include <boost/spirit/home/phoenix/container.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <iostream>
#include <string>
#include <tuple>
typedef std::tuple<double, double, double> vertex;
typedef std::vector<vertex> Vertices;
template <typename Iterator>
bool vector_creator(Iterator first, Iterator last, Vertices& vector)
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;
bool r = qi::phrase_parse(first, last,
(
'f' >> *(qi::double_ >> '/' >> qi::double_>> '/' >> qi::double_)
[
phoenix::push_back(phoenix::ref(vector),
phoenix::construct<vertex>(qi::_1, qi::_2 , qi::_3))
]
), qi::space);
return r;
}
int main()
{
std::string str;
while (getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
{
break;
}
Vertices Vertices;
if (vector_creator(str.begin(), str.end(), Vertices))
{
std::cout << "Parsing succeeded: " << Vertices.size() << std::endl;
}
else
{
std::cout << "Parsing failed." << std::endl;
}
}
return 0;
}
Program run:
> a.exe
f 1/1.2/-3 0.5/2.3/0 2./5/6 .3/.2/9888
Parsing succeeded: 4
Upvotes: 2
Reputation: 52405
std::strtol is quite fast:
const char* l = line.c_str() + 2;
while (l)
{
char* c;
long num = std::strtol(l, &c, 10);
if (l == c)
{
break;
}
//do something with vertex
l = c + 1; // move past the slash
}
Upvotes: 2
Reputation: 33655
Use std::strtol
, this is quite neat in that it will return the end of the current parse, and you can continue from there. So let's say that you guarantee that you read three digits each time, something like the following sketch could work..
char *p = line.c_str() + 1;
while (p)
{
long v0 = std::strtol(++p, &p, 0); // at the end, p points to '/'
long v1 = std::strtol(++p, &p, 0); // at the end, p points to '/'
long v2 = std::strtol(++p, &p, 0); // at the end, p points to ' '
// On the last one, p will be null...
}
Upvotes: 4
Reputation: 25396
You can do it this way:
if ( sscanf( Line.c_str(), "%2s %d/%d %d/%d %d/%d %d/%d", Prefix, &A1, &A2, &B1, &B2, &C1, &C2, &D1, &D2 ) == 9 )
{
A3 = B3 = C3 = 0;
...
}
else if ( sscanf( Line.c_str(), "%2s %d/%d/%d %d/%d/%d %d/%d/%d", Prefix, &A1, &A2, &A3, &B1, &B2, &B3, &C1, &C2, &C3 ) == 10 )
{
...
}
else if ( sscanf( Line.c_str(), "%2s %d//%d %d//%d %d//%d", Prefix, &A1, &A3, &B1, &B3, &C1, &C3 ) == 7 )
{
A2 = B2 = C2 = 0;
...
}
else if ( sscanf( Line.c_str(), "%2s %d/%d %d/%d %d/%d", Prefix, &A1, &A2, &B1, &B2, &C1, &C2 ) == 7 )
{
A3 = B3 = C3 = 0;
...
}
Upvotes: -1