标记一个字符串,并将每个分隔符放在它自己的标记中
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:要解决此问题,您可以在 default
的 default
分支中的 if
语句中添加 --end
:IdeOne。
这里的问题是,如果您找到的一种标记是LABEL
,您会多吞一个符号。这就是为什么x
之后的=
符号被忽略的原因。当你在它们之间添加一个空格时,这个空格会被忽略,=
符号会被正确解析。
LABEL
类型标记之后的符号被吞掉,原因如下:您忽略了字符 #end
。对于所有其他类型的标记,这非常好,因为在这种情况下end
表示标记的最后一个字符,但对于LABEL
类型的标记,end
等于标记之后的第一个字符的数量。
【讨论】:
但是输入中没有注释? 你说得对,可能是LABEL
,不是评论。用于表示字符串的Token
的类型,在switch
的default
分支中处理。
它使str[start]
越界(结束和开始下溢)。以上是关于标记一个字符串,并将每个分隔符放在它自己的标记中的主要内容,如果未能解决你的问题,请参考以下文章