C++ 从std::string中提取整数

C++ 从std::string中提取整数,c++,c++11,C++,C++11,我正在编写简单的OBJ加载程序,遇到了下一个问题-我必须从nextstd::string中提取整数: f v0/vt0/vn0 v1/vt1/vn0 ... vk/vtk/vnk 其中,vk,vtk,vnk是int值,/和值之间没有空格,组之间只有一个空格 由于文件可能相当大,并且这种类型的行可能出现100000次以上,因此我需要一种有效的方法来从像这样的字符串中提取整数 编辑: 正如Jesse所问,这是我目前的方法(我假设数据的格式是正确的!): 您可以这样做: if ( sscanf( L

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

f v0/vt0/vn0 v1/vt1/vn0 ... vk/vtk/vnk
其中,
vk
vtk
vnk
是int值,
/
和值之间没有空格,组之间只有一个空格

由于文件可能相当大,并且这种类型的行可能出现100000次以上,因此我需要一种有效的方法来从像这样的字符串中提取整数

编辑:

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


您可以这样做:

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

使用
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...
}
速度相当快:

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
}

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

下面是一个通过将顶点放入向量来解决问题的示例。如果要调用其他函数,请参阅phoenix文档:

我承认菲尼克斯/精神号的入场费很高,但我认为这是值得的

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

我不认为它比循环字符串和做数学运算更有效。@Jesse,立即假设这个人懒惰是没有多大帮助的。看看他的名声,他已经帮助了很多人,几乎没有懒人的特点。也许他没有时间,或者他不想解决一些问题,而是采取了一种全新的方法。如果你不想帮助他,请跳过这个问题,而不要消极。我不会使用sscanf,效率非常低(一般来说)。@Felics一个简单的有限状态机(FSM)的效率和你将得到的一样高。我只是手工编写代码,因为格式似乎并不复杂,但如果您愿意(例如Ragel),有一些工具可以为您生成FSM代码。@Rob great minds等。错误检查代码在哪里?(您如何知道
std::strtol
读取整数是否失败)@MatthieuM。你不能把一切都给别人!;)有效的注释,因为strtol有一个可怕的界面<代码>标准::stoi更好。但是一定要检查
*p='\0'
,因为这是这里的正常终止条件。是否应该
l=c+1
移过斜杠?这将一次增加1个字符,转换
“vt123”
将产生123、23和3个字符。@Nim,MSalters:谢谢您的反馈。更正。
#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