【问题标题】:Parsing C++ strings解析 C++ 字符串
【发布时间】:2015-03-23 23:00:12
【问题描述】:

这里有很多关于发布字符串的帖子,但实际上似乎不符合我的目的。

我正在使用std::string 和所有 C++ 标准库,并且我有一个使用以下协议的文本文件:

TEXT1:TEXT2-TAB-TEXT3:TEXT4 TEXT5

-TAB-\t

我想把所有的文本变成字符串(也可以是一个数组)。文件中的所有行都是这样写的,我尝试使用istringstream,但它没有任何功能,例如:iss >> text1 >> ":" >> text2 >> "\t" >> text3 >> ":" >> text4 >> " " >> text5

我真的需要使用find等的基本功能进行解析吗?那只是大量的工作(因为我有几个以不同格式编写的文件,我需要为所有这些文件创建一个通用函数),但如果我别无选择,我会这样做。

那么...有没有办法通过以下方式解析字符串,使用字符串之间的已知字符?它不是一个特定的分隔符,因为每一行都包含几个分隔符(一次是空格,然后是冒号等等)。我想使用 C++ 标准库,而不是 Boost 等任何外部库。

编辑: C++11。

【问题讨论】:

  • 我们至少可以假设哪个 C++ 标准? C++11? C++14?
  • @PaulR C++11,我的错。
  • 所呈现的分隔符的order 是强制性的吗?即,您希望 exactly 一个冒号,然后是一个制表符,然后是一个冒号,然后是一个空格,然后是最后一个标记?即使在通用解决方案中,它也可以采用任何一种方式,但这需要更多的工作。
  • 你可以写一个助手,让你写出类似iss >> num1 >> expect('\t') >> num2;的东西

标签: c++ string parsing c++11


【解决方案1】:

由于您使用的是 C++11 并且您的文本行遵守协议,因此用于模式匹配和信息提取的工具是正则表达式库中的功能。

与您的协议匹配的模式可能看起来像这样......

\w+:\w+-\t-\w+:\w+\s\w+

... 使用默认的 ECMAScript 语法。还有一些其他的。

接下来,使用原始字符串文字来初始化正则表达式对象...

正则表达式 pat{R("\w+:\w+-\t-\w+:\w+\s\w+")};

所以现在你的代码可以看起来像这样......

#include<regex>
...

regex pat{R("\w+:\w+-\t-\w+:\w+\s\w+")};
smatch m;

while (cin >> str) {  // where str is your line of formatted text
    bool match = regex_search(str, m, pat);
    for (int i = 0; i < m.size(); i++) {
        cout << m[i].str() << " "; // to make sure each component was matched
    }   
}

顺便说一下,smatch就像一个容器,可以迭代,非常方便。

注意:以上代码不保证有效,仅供参考。

【讨论】:

  • 非常感谢!这正是我想要的。
  • 但是,您需要使用最新的编译器和 libc++ 才能正常工作&lt;regex&gt;
  • 很好的答案!有趣的是,C++ 中的正则表达式是根据 ecmascript 语法建模的。 \t 也是正则表达式语法的一部分。有趣。
【解决方案2】:

因为你有一个单一的、固定的字符来标记每个字段的结尾,任何像正则表达式这样的东西都过分了。我只是使用std::getline 来读取每个字段。

我首先为一行中的字段定义一个结构,然后重载 operator&gt;&gt; 以读取其中一个结构:

struct line { 
    std::string text1, text2, text3, text4, text5;

    friend std::istream &operator>>(std::istream &is, line &l) {
        std::getline(is, l.text1, ':');
        std::getline(is, l.text2, '\t');
        std::getline(is, l.text3, ':');
        std::getline(is, l.text4, ' ');
        std::getline(is, l.text5);
        return is;
    }
};

这样,你可以读到这样一行:

line x;

std::cin >> x;

...或者,如果您有一个充满这样行的整个文件,您可以将它们全部读入向量中,例如:

std::ifstream infile("whatever.dat");

std::vector<line> lines {
    std::istream_iterator<line>(lines),
    std::istream_iterator<line>()
};

【讨论】:

    【解决方案3】:

    您可能应该使用std::getline 读取整行,然后解析该行,例如使用findfind_first_ofstd::string 方法查找'\t' 字符。

    如果可能,至少切换到C++11,因为 C++11 的许多特性可以让您编写更少的代码。特别是来自&lt;algorithm&gt;std::find 在与匿名lambda 一起使用时很有帮助。

    当然,您应该更正式地定义可接受的输入(可能使用一些EBNF 符号,至少在 cmets 中)。特别是,您的 TEXT1TEXT2TEXT3TEXT4TEXT5。用什么编码? (UTF-8 有多字节字符!)。

    如果输入规范比较复杂,可以考虑使用一些parser generator,比如ANTLR等。

    【讨论】:

    • 我使用的是 C++11。关于字符,我使用普通字符(基本字符),text1和text3包含数字和点,text2和text4只包含数字,text5包含常规ABC。
    • char,我的意思是普通的 ASCII。但文件中只会显示字母数字(除了冒号和制表符)。
    • 因此您需要更正式、更系统地指定可接受的输入!明确一个问题是解决方案的一半
    猜你喜欢
    • 2019-03-11
    • 1970-01-01
    • 1970-01-01
    • 2015-10-17
    • 2011-02-15
    • 2019-05-03
    • 1970-01-01
    • 2010-10-11
    • 1970-01-01
    相关资源
    最近更新 更多