C ++字符串解析思路

时间:2011-02-14 03:36:00

标签: c++ string parsing

我有另一个程序的输出,这个程序更像是人类可读而不是机器可读,但我还是打算解析它。这没什么太复杂的。

然而,我想知道在C ++中最好的方法是什么。这更像是一种“一般惯例”的问题。

我研究了Boost.Spirit,甚至让它运转了一下。那东西太疯狂了!如果正在设计我正在阅读的语言,那么它可能是正确的工具。但实际上,考虑到它的极端编译时间,当我做错任何事情时,g ++会出现几页错误,这不是我需要的。 (我也不太需要运行时性能。)

考虑使用C ++运算符<<,但这似乎毫无价值。如果我的文件中有“John有5个小部件”这样的行,而其他人“Mary在459 Ramsy street工作”,我怎么能确保我的程序中有第一个类型的行,而不是第二个类型?我必须阅读整行,然后使用像string::findstring::substr这样的内容。

离开sscanf。它会很好地处理上述案件

if( sscanf( str, "%s has %d widgets", chararr, & intvar ) == 2 )
      // then I know I matched "foo has bar" type of string, 
      // and I now have the parameters too

所以我只是想知道我是否遗漏了某些内容,或者C ++是否真的没有太多的内置替代方案。

7 个答案:

答案 0 :(得分:3)

sscanf确实听起来非常适合您的要求:

  • 您可能会进行一些冗余解析,但您没有禁止
  • 的性能要求
  • 它对不同输入词的需求进行本地化,并允许将非字符串值直接解析为类型变量,使不同的输入格式易于理解

一个潜在的问题是它容易出错,如果你有很多不断变化的解析短语,那么测试工作和风险就会令人担忧。保持sscanf的精神,但使用istream来确保类型安全:

#include <iostream>
#include <sstream>

// Str captures a string literal and consumes the same from an istream...
// (for non-literals, better to have `std::string` member to guarantee lifetime)
class Str
{
  public:
    Str(const char* p) : p_(p) { }
    const char* c_str() const { return p_; }
  private:
    const char* p_;
};

bool operator!=(const Str& lhs, const Str& rhs)
{
    return strcmp(lhs.c_str(), rhs.c_str()) != 0;
}

std::istream& operator>>(std::istream& is, const Str& str)
{
    std::string s;
    if (is >> s)
        if (s.c_str() != str)
            is.setstate(std::ios_base::failbit);
    return is;
}

// sample usage...

int main()
{
    std::stringstream is("Mary has 4 cats");
    int num_dogs, num_cats;

    if (is >> Str("Mary") >> Str("has") >> num_dogs >> Str("dogs"))
    {
        std::cout << num_dogs << " dogs\n";
    }
    else if (is.clear(), is.seekg(0), // "reset" the stream...
             (is >> Str("Mary") >> Str("has") >> num_cats >> Str("cats")))
    {
        std::cout << num_cats << " cats\n";
    }
}

答案 1 :(得分:2)

GNU工具flexbison是非常强大的工具,您可以使用这些工具沿着Spirit的路线但是(根据某些人)更容易使用,部分原因是错误报告有点更好,因为这些工具有自己的编译器。这个,或Spirit,或其他一些解析器生成器,是“正确”的方法,因为它为您提供了最大的灵活性。

如果您正在考虑使用strtok,您可能需要查看stringstream,它会拆分空格,并允许您在字符串,基元等之间进行一些很好的格式转换。它也可以插入到STL算法中,避免了原始C风格字符串内存管理的所有混乱细节。

答案 2 :(得分:1)

我在C ++中编写了大量的解析代码。它的效果非常好,但我自己编写代码并且不依赖于其他人编写的更通用的代码。 C ++没有提供已编写的大量代码,但它是编写此类代码的一种很好的语言。

我不确定你的问题是什么,只是你想找到一个已经编写过的代码,它可以满足您的需求。部分问题在于您没有真正描述您的需求,或者就此问题提出了问题。

如果您可以更具体地提出问题,我很乐意尝试提供更具体的答案。

答案 3 :(得分:1)

我使用过Boost.Regex(我认为它也是tr1 :: regex)。易于使用。

答案 4 :(得分:0)

总是有strtok()我想

答案 5 :(得分:0)

查看strtok

答案 6 :(得分:0)

根据您要解析的内容,您可能需要正则表达式库。 请参阅msdnearlier question

就个人而言,再次根据确切的格式,我会考虑使用perl初始转换为更易于机器读取的格式(例如变量记录CSV),然后更容易导入C ++。

如果坚持使用C ++,则需要:

  1. 识别记录 - 希望只是一个 线
  2. 确定记录的类型 - 使用正则表达式
  3. 解析记录 - scanf很好
  4. 以下行的基类:

    class Handler
    {
    public:
        Handler(const std::string& regexExpr)
            : regex_(regexExpr)
        {}
        bool match(const std::string& s)
        {
            return std::tr1::regex_match(s,regex_);
        }
        virtual bool process(const std::string& s) = 0;
    private:
        std::tr1::basic_regex<char> regex_;
    };
    

    为每种记录类型定义派生类,在集合中粘贴每个记录的实例并搜索匹配。

    class WidgetOwner : public Handler
    {
    public:
        WidgetOwner()
            : Handler(".* has .* widgets")
        {}
        virtual bool process(const std::string& s) 
        {
            char name[32];
            int widgets= 0;
            int fieldsRead = sscanf( s.c_str(),  "%32s has %d widgets", name, & widgets) ;
    
            if (fieldsRead == 2)
            {
                std::cout << "Found widgets in " << s << std::endl;
            }
            return fieldsRead == 2;
        }
    };
    
    struct Pred 
    {
        Pred(const std::string& record)
            : record_(record)
        {}
        bool operator()(Handler* handler)
        {
            return handler->match(record_);
        }
        std::string record_;
    };
    
    std::set<Handler*> handlers_;
    handlers_.insert(new WidgetOwner);
    handlers_.insert(new WorkLocation);
    
    Pred pred(line);
    std::set<Handler*>::iterator handlerIt = 
         std::find_if(handlers_.begin(), handlers_.end(), pred);
    if (handlerIt != handlers_.end())
        (*handlerIt)->process(line);