对字符串进行标记,并将每个分隔符放入其自己的标记中

时间:2016-08-04 14:30:33

标签: c++ string tokenize stdstring lexer

期望的行为:

  
      
  • 忽略'#'后的所有内容# =评论)
  •   
  • 空行不会创建令牌。
  •   
  • '{'创建BLOCK_OPEN类型的令牌。
  •   
  • '}'创建BLOCK_CLOSE类型的令牌。
  •   
  • '='创建EQUALS类型的标记。
  •   
  • 其他所有内容都会创建LABEL类型的令牌。
  •   
  • 代币不得有空格
  •   

对于大多数输入,我的标记化功能完美无瑕。除了一个错误:

  

show_position = {x = -9 y = 78}

注意缺少空格!

返回的向量缺少"=""x"之间的"-9"

如何修复此错误?我试过调试但无法弄清楚我搞砸了什么。一双新鲜的眼睛是一种恩惠。

这就是我的标记方式:

std::vector<Token> tokenizeLine(const std::string str)
{
    std::vector<Token> tokens;

    std::string::size_type start = 0;
    std::string::size_type end   = 0;
    while (end != std::string::npos)
    {
        enum POSES
        {
            EQUALS,
            OPEN,
            CLOSE,
            SPACE,
            EOL,
            RETURN,
            TAB,
            COMMENT,
            POSES_SIZE
        };
        std::string::size_type pos[] =
        {
            str.find('=', start),
            str.find('{', start),
            str.find('}', start),
            str.find(' ', start),
            str.find('\n', start),
            str.find('\r', start),
            str.find('\t', start),
            str.find('#', start)
        };
        end = *std::min_element(pos, &pos[POSES_SIZE]);

        switch (str[start])
        {
        case('=') :
            tokens.push_back(Token(Token::EQUALS, "="));
            break;
        case('{') :
            tokens.push_back(Token(Token::BLOCK_OPEN, "{"));
            break;
        case('}') :
            tokens.push_back(Token(Token::BLOCK_CLOSE, "}"));
            break;
        case(' ') :
        case('\n') :
        case('\r') :
        case('\t'):
            break;
        case('#') :
            return tokens;
            break;
        default:
            if(str.substr(start, end - start).length() > 0)
                tokens.push_back(Token(Token::LABEL, str.substr(start, end - start)));
        }

        // If at end, use start=maxSize.  Else use start=end+delimiter.
        start = ((end > (std::string::npos - sizeof(char)))
            ? std::string::npos : end + sizeof(char));
    }

    return tokens;
}

这是您可以在舒适的家中跑步的地方:

std::vector<std::string> tokenizeLine(const std::string str)
{
    std::vector<std::string> tokens;

    std::string::size_type start = 0;
    std::string::size_type end   = 0;
    while (end != std::string::npos)
    {
        enum POSES // Deliminators
        {
            EQUALS,
            OPEN,
            CLOSE,
            SPACE,
            EOL,
            RETURN,
            TAB,
            COMMENT,
            POSES_SIZE
        };
        std::string::size_type pos[] =
        {
            str.find('=', start),
            str.find('{', start),
            str.find('}', start),
            str.find(' ', start),
            str.find('\n', start),
            str.find('\r', start),
            str.find('\t', start),
            str.find('#', start)
        };
        end = *std::min_element(pos, &pos[POSES_SIZE]);

        switch (str[start])
        {
        case('=') :
            tokens.push_back("=");
            break;
        case('{') :
            tokens.push_back("{");
            break;
        case('}') :
            tokens.push_back("}");
            break;
        case(' ') :
        case('\n') :
        case('\r') :
        case('\t'):
            break;
        case('#') :
            return tokens;
            break;
        default:
            if(str.substr(start, end - start).length() > 0)
                tokens.push_back(str.substr(start, end - start));
        }

        // If at end, use start=maxSize.  Else use start=end+delimiter.
        start = ((end > (std::string::npos - sizeof(char)))
            ? std::string::npos : end + sizeof(char));
    }
    return tokens;
}

2 个答案:

答案 0 :(得分:1)

这听起来像regex_iterator的工作!对于无上下文的语言,例如您正在尝试使用的语言,很难击败正则表达式。因此,不要试图将代码纠缠在一起,抛弃它,并使用正确的工具来完成工作。

这个正则表达式为每个你想要的标记都有不同的捕获:

\s*(?:\n|(#[^\n]*)|(\{)|(\})|(=)|([^{}= \t\r\n]+))

Live Example

给出类似的输入,const auto input = "#Comment\n\nshow_position = { x=-9 y =78 }"s您可以简单地将其解析为:

vector<Tokens> tokens;

for_each(sregex_iterator(cbegin(input), cend(input), re), sregex_iterator(), [&](const auto& i) {
    if (i[1].length() > 0U) {
        tokens.emplace_back(Token::COMMENT, i[1]);
    } else if (i[2].length() > 0U) {
        tokens.emplace_back(Token::BLOCK_OPEN, "{"s);
    } else if (i[3].length() > 0U) {
        tokens.emplace_back(Token::BLOCK_CLOSE, "}"s);
    } else if (i[4].length() > 0U) {
        tokens.emplace_back(Token::EQUALS, "="s);
    } else if (i[5].length() > 0U) {
        tokens.emplace_back(Token::LABEL, i[5]);
    }
});

Live Example

答案 1 :(得分:-1)

TL; DR:要解决此问题,您可以将--end添加到if default的{​​{1}}分支中的switch语句中。< / p>

这里的问题是,如果您找到的某种令牌是LABEL,那么您可以吞下一个符号。这就是忽略=之后的x符号的原因。当您在它们之间添加空格时,将忽略此空白,并正确解析=符号。

由于以下原因,吞下LABEL类型令牌后面的符号:忽略char#end。对于所有其他类型的令牌,这非常好,因为在这种情况下end表示令牌的最后一个字符,但对于LABEL类型的令牌end等于后面的第一个字符的数量令牌。