标记一个字符串,并将每个分隔符放在它自己的标记中
Tokenize a string, and put each delimiter in it's own token
期望的行为:
- 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 }
注意缺少空格![=23=]
返回的矢量在 "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;
}
TL;DR:要解决此问题,您可以将 --end
添加到 switch
的 default
分支中的 if
语句:IdeOne .
这里的问题是,如果您找到的一种令牌类型是 LABEL
,那么您吞下的符号多于应有的数量。这就是 x
之后的 =
符号被忽略的原因。当您在它们之间添加一个空格时,这个空格将被忽略,并且 =
符号被正确解析。
LABEL
类型标记之后的符号被吞掉了,原因如下:您忽略了字符#end
。对于所有其他类型的标记,这非常好,因为 end
在这种情况下代表标记的最后一个字符,但对于 LABEL
类型的标记, end
等于之后的第一个字符的数量令牌。
这听起来像是 regex_iterator
的工作!对于上下文无关语言,就像您尝试使用的语言一样,很难击败正则表达式。因此,与其试图将代码整理成形状,不如扔掉它,并使用正确的工具来完成工作。
此正则表达式对您所需的每个标记都有不同的捕获:
\s*(?:\n|(#[^\n]*)|(\{)|(\})|(=)|([^{}= \t\r\n]+))
给定这样的输入,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]);
}
});
期望的行为:
- 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 }
注意缺少空格![=23=]
返回的矢量在 "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;
}
TL;DR:要解决此问题,您可以将 --end
添加到 switch
的 default
分支中的 if
语句:IdeOne .
这里的问题是,如果您找到的一种令牌类型是 LABEL
,那么您吞下的符号多于应有的数量。这就是 x
之后的 =
符号被忽略的原因。当您在它们之间添加一个空格时,这个空格将被忽略,并且 =
符号被正确解析。
LABEL
类型标记之后的符号被吞掉了,原因如下:您忽略了字符#end
。对于所有其他类型的标记,这非常好,因为 end
在这种情况下代表标记的最后一个字符,但对于 LABEL
类型的标记, end
等于之后的第一个字符的数量令牌。
这听起来像是 regex_iterator
的工作!对于上下文无关语言,就像您尝试使用的语言一样,很难击败正则表达式。因此,与其试图将代码整理成形状,不如扔掉它,并使用正确的工具来完成工作。
此正则表达式对您所需的每个标记都有不同的捕获:
\s*(?:\n|(#[^\n]*)|(\{)|(\})|(=)|([^{}= \t\r\n]+))
给定这样的输入,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]);
}
});