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

Posted

技术标签:

【中文标题】标记一个字符串,并将每个分隔符放在它自己的标记中【英文标题】:Tokenize a string, and put each delimiter in it's own token 【发布时间】:2016-08-04 14:30:32 【问题描述】:

期望的行为:

'#' 之后的所有内容都将被忽略# = 评论)。 空行不会创建标记。 '' 创建一个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;

【问题讨论】:

@NathanOliver 给定的代码没有按预期运行,因此在代码审查中会偏离主题。 抱歉不清楚。我编辑了问题以澄清问题。 @IvanRubinson 第二个代码运行良好:ideone.com/i1tRr8 请提供 MCVE。 @alexeykuzmin0 注意空格!省略空格时会发生特定错误。您的粘贴包含我的字符串没有的空格。 【参考方案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

【讨论】:

【参考方案2】:

TL;DR:要解决此问题,您可以在 defaultdefault 分支中的 if 语句中添加 --end:IdeOne。

这里的问题是,如果您找到的一种标记是LABEL,您会多吞一个符号。这就是为什么x 之后的= 符号被忽略的原因。当你在它们之间添加一个空格时,这个空格会被忽略,= 符号会被正确解析。

LABEL 类型标记之后的符号被吞掉,原因如下:您忽略了字符 #end。对于所有其他类型的标记,这非常好,因为在这种情况下end 表示标记的最后一个字符,但对于LABEL 类型的标记,end 等于标记之后的第一个字符的数量。

【讨论】:

但是输入中没有注释? 你说得对,可能是LABEL,不是评论。用于表示字符串的Token 的类型,在switchdefault 分支中处理。 它使str[start] 越界(结束和开始下溢)。

以上是关于标记一个字符串,并将每个分隔符放在它自己的标记中的主要内容,如果未能解决你的问题,请参考以下文章

文本到语音和导航

如何将表格视图数组中的每个字符串分隔到自己的单元格中?

没有 DIV 标记的 TextArea 无法正常工作

Java输入 StreamTokenizer

解析字符串并将标记作为参数传递

c_cpp 根据空间拆分字符串并将每个标记转换为大写。