c++ - 标记一个字符串,并将每个分隔符放在它自己的标记中

标签 c++ string tokenize stdstring lexer

期望的行为:

  • Everything after a '#' is ignored (# = comment).
  • Empty lines don't create tokens.
  • '{' creates a token of type BLOCK_OPEN.
  • '}' creates a token of type BLOCK_CLOSE.
  • '=' creates a token of type EQUALS.
  • Everything else creates a token of type LABEL.
  • Tokens must not have empty space(s)

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

show_position = { x=-9 y =78 }

注意缺少空格!

返回的 vector 在 "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;
}

最佳答案

这听起来像是一份 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

关于c++ - 标记一个字符串,并将每个分隔符放在它自己的标记中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38770249/

相关文章:

c - 如何根据c中的位置打印字符串?

c++ - 将 const 添加到 size_t 会导致编译失败是标准行为吗?

c++ - Eclipse CDT 中的 POSIX IPC 链接器错误

c++ - 在 GLFW 中抓取鼠标光标

SQL 为什么使用 isnull(field, '' ) <> '' ?

java - 如何在 Java 中实现字符串格式化机制?

c++ - 使用 CryptoAPI 从 PFX 证书获取 X509 数据

regex - 如何在 Swift 中修剪字符串的开头和结尾?

mysql - 从 MySQL SELECT 中删除数字

java - java中的正则表达式