4

我正在编写简单的 OBJ 加载器,但遇到了下一个问题 - 我必须从下一个中提取整数std::string

f v0/vt0/vn0 v1/vt1/vn0 ... vk/vtk/vnk

其中vk, vtk,vnk是 int 值,并且值之间没有空格/,组之间只有一个空格。

由于文件可能非常大,并且这种类型的行可能出现超过 100000 次,我需要一种有效的方法来从像这样的字符串中提取整数。

编辑:

正如 Jesse 所问,这是我目前的方法(我假设数据格式正确!):

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

4 回答 4

4

使用std::strtol,这非常简洁,因为它将返回当前解析的结尾,您可以从那里继续。因此,假设您保证每次读取三个数字,类似下面的草图可以工作..

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...
}
于 2013-11-14T13:01:43.370 回答
2

std::strtol相当快:

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
}
于 2013-11-14T13:02:51.080 回答
2

使用提升精神,它更强大,更容易进化。

http://www.boost.org/doc/libs/1_54_0/libs/spirit/doc/html/spirit/qi/tutorials/semantic_actions.html

这是一个通过将顶点放在向量中来解决问题的示例。如果要调用另一个函数,请参阅 phoenix doc:

http://www.boost.org/doc/libs/1_55_0/libs/spirit/phoenix/doc/html/phoenix/starter_kit.html#phoenix.starter_kit.lazy_functions

我承认 Phoenix/Spirit 的入门成本很高,但我认为这是值得的。

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

程序运行:

> a.exe
f 1/1.2/-3 0.5/2.3/0 2./5/6 .3/.2/9888
Parsing succeeded: 4
于 2013-11-14T16:11:19.050 回答
-1

你可以这样做:

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;
    ...
}
于 2013-11-14T12:55:15.463 回答