【问题标题】:C++, parsing a string line with space as one unitC++,解析以空格为单位的字符串行
【发布时间】:2016-03-29 13:13:28
【问题描述】:

我正在用我的编程语言编写一个翻译器,这是一种简单的 C++,我对解析一行有疑问。在 C++ 中,我们将一些对象定义为:

class Item{
    string Item;
    string Item2;
};

我想使用关键字add 以我的语言创建相同的对象:

add "Item Item", "Item2 Item2";

如您所见,string 变量“Item*”可能是带有空格的行。

但我需要解析它并制作一个数组命令堆栈。在示例中,我想解析该行以生成 3 的数组,如下所示:[add, Item Item, Item2 Item2]。所以,我需要保存“Item Item”之间的空格并在解析时将其计为一个字符串行,但仍然使用空格作为add和第一个“Item*”之间的分隔符。我该怎么做?

【问题讨论】:

  • 计算开盘价和收盘价。如果引号打开,则空格会附加到最后一个字符串。如果引号未打开,则空格是分隔符并被跳过。
  • 将其作为单行读取(使用例如std::getline)然后您必须以其他方式解析字符串。删除第一个空格分隔的单词和终止分号应该很容易。然后在逗号处拆分其余部分,并删除每个字符串引号。
  • 试试 Boost.Spirit。它肯定对你有帮助。
  • <iomanip> 标头现在具有操纵器 std::quoted()
  • 另外,如果你想创建自定义语言,我认为你应该阅读 bison/flex 或一些类似的工具。 Yacc/lex 或 bison/flex 非常强大,一旦你了解了结构,使用起来很有趣。

标签: c++ arrays string parsing delimiter


【解决方案1】:

您需要将这些行分解为命令,这可以通过getline 来完成。然后你会想用quoted 将命令分开。 (请注意,quoted 仅适用于 C++14。因此,如果您没有该解决方案,则此解决方案将不起作用。)

这种方法的一个问题是您在语言中使用空格 逗号作为分隔符。所以你必须提取逗号。但这可以通过一个简单的if 来完成,给您留下如下内容:

vector<vector<string>> result;
string command;

while(getline(input, command, ';')) {
    istringstream i(command);
    string element;
    result.resize(result.size() + 1);

    while( i >> quoted(element)){
        if(element != ",") result.back().push_back(element);
    }
}

其中input 是带有命令的istringstream

Live Example

【讨论】:

  • 如果引用的元素包含逗号或分号怎么办?
  • @ZDF 在 C++ 中,规则将是引用元素的一部分。我假设这里同样适用。
  • 我没有注意到quotedquoted 不是 c++14 的一部分吗?
  • @ZDF 是的,很好,我会放一个限定符。不是每个人都使用最近两年开发的技术;)
【解决方案2】:

如果您使用 Boost,则可以使用 split 函数。我没有发现所有潜在的错误,但它以相当易于理解的代码给出了正确的答案。我所做的是在引号上拆分代码。在引号之前是动作,在第一个引号之间是第一项,然后是逗号,在第二组引号之间是第二项:

#include <iostream>
#include <vector>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>

class Item_container
{
    public:
        Item_container(const std::string& s)
        {
            std::cout << "Input string: " << s << std::endl;

            std::vector<std::string> string_vector;
            boost::split(string_vector, s, boost::is_any_of("\""));

            // Below, error checking should be implemented
            stack.push_back(string_vector[0]);
            stack.push_back(string_vector[1]);
            stack.push_back(string_vector[3]);
        }

        std::vector<std::string> get_stack() const { return stack; }

    private:
        std::vector<std::string> stack;
};

int main()
{
    Item_container item_container("add \"Item Item\", \"Item2 Item2\";");

    for (auto &s : item_container.get_stack())
        std::cout << s << std::endl;

    return 0;
}

【讨论】:

    【解决方案3】:

    这是一个小标记器。这只是一个示例;它没有错误检查,因此可能会因意外输入而崩溃。包含文件有 iostream、string、vector 和 ctype.h。

    enum st
    {
        inSpace,
        inToken,
        inString
    };
    
    static st newstate(const char* p)
    {
        if (isalpha(*p))
        {
            return inToken;
        }
        if ('"' == *p)
        {
            return inString;
        }
        return inSpace;
    }
    
    int main(int argc, const char * argv[]) {
        // insert code here...
        std::cout << "Hello, World!\n";
        char line[128];
        std::cin.getline(line, sizeof(line));
        st state = inSpace;
        char* p = line;
        char* ptok = nullptr; // Will point to the beginning of a token
        std::vector<std::string*> sym;
    
        while(*p)
        {
            switch(state)
            {
                case inSpace:
                    while(isspace(*p) || (',' == *p))
                    {
                        ++p;
                    }
                    state = newstate(p);
                    break;
    
                case inString:
                    ptok = p; // Token includes opening quote
                    while('"' != *++p);
                    sym.push_back(new std::string(ptok, p + 1));
                    state = newstate(++p);
                    break;
    
                case inToken:
                    ptok = p;
                    while(isalpha(*++p));
                    sym.push_back(new std::string(ptok, p));
                    state = newstate(p);
                    break;
    
                default:
                    std:: cout << "Error\n";
            }
        }
    
        for(int i = 0; sym.size() > i; ++i)
        {
            std::cout << "Symbol #" << i + 1 << " = " << *(sym[i]) << std::endl;
        }
        return 0;
    }
    

    【讨论】:

    • 我很好奇为什么我的回答被否决了。我测试了它;它运行正常。它怎么不能成为一个好的答案?
    猜你喜欢
    • 2013-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-18
    • 1970-01-01
    • 2019-02-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多