为什么这个分词器 return 的值不正确?
Why does this tokenizer return incorrect values?
当标记化 JSON 字符串时,它 return 是一个不正确的值,就像它一次连接多个值一样(即 "username": "Azoraqua", "age": }
(它应该是 IDENTIFIER(2 次)和STRING_LITERAL(分别为 1 次),请注意它确实 return age
号作为它自己的标记(分别为 INTEGER_LITERAL)。
我已经尝试了几种方法来实现正确的行为:
- 更改一些与 IDENTIFER 和 STRING_LITERAL.
相关的正则表达式
- 改变一些实际的标记化逻辑。
private static final Set<TokenData> tokenDatas = new LinkedHashSet<>();
static {
tokenDatas.add(new TokenData(Pattern.compile("^(,:)"), TokenType.TOKEN));
tokenDatas.add(new TokenData(Pattern.compile("^(\{)"), TokenType.BEGIN_OBJECT));
tokenDatas.add(new TokenData(Pattern.compile("^(})"), TokenType.END_OBJECT));
tokenDatas.add(new TokenData(Pattern.compile("^(\[)"), TokenType.BEGIN_ARRAY));
tokenDatas.add(new TokenData(Pattern.compile("^(])"), TokenType.END_ARRAY));
tokenDatas.add(new TokenData(Pattern.compile("^(\".*\":)"), TokenType.IDENTIFIER));
tokenDatas.add(new TokenData(Pattern.compile("^(\".*\")"), TokenType.STRING_LITERAL, (s) -> s.substring(1, s.length() - 1)));
tokenDatas.add(new TokenData(Pattern.compile("^((-)?[0-9]+)"), TokenType.INTEGER_LITERAL));
tokenDatas.add(new TokenData(Pattern.compile("^((-)?[0-9]*(\.)[0-9]+)"), TokenType.DOUBLE_LITERAL));
tokenDatas.add(new TokenData(Pattern.compile("^(true|false)", Pattern.CASE_INSENSITIVE), TokenType.BOOLEAN_LITERAL));
}
@Override
public Token next() {
str = str.trim();
if (pushback) {
pushback = false;
return lastToken;
}
if (str.isEmpty()) {
return (lastToken = new Token(TokenType.EMPTY, ""));
}
for (TokenData data: tokenDatas) {
Matcher matcher = data.pattern.matcher(str);
if (matcher.find()) {
String token = matcher.group().trim();
str = matcher.replaceFirst("");
if (data.action != null) {
token = data.action.apply(token);
}
return (lastToken = new Token(data.type, token));
}
}
throw new IllegalStateException("Could not parse " + str);
}
当输入为 {"username": "Azoraqua", "age": 21}
时,输出应为:
1. BEGIN_OBJECT ( {
)
2. 标识符 ( "username":
)
3. STRING_LITERAL ( "Azoraqua"
)
4.令牌(,
)
5.标识符("age"
)
6. INTEGER_LITERAL ( 21
)
7. END_OBJECT ( }
)
我该如何解决这个问题?
问题最有可能出现在这一行:
tokenDatas.add(new TokenData(Pattern.compile("^(\".*\":)"), TokenType.IDENTIFIER));
正则表达式是贪婪的。这意味着他们将尽可能多地匹配。
因此,对于这样的字符串:
"username": "Azoraqua", "age": 21 }
正则表达式的 .*\":
部分将从 "username" 中的 u 开始匹配所有字符,直到并包括最后一个可能的 \":
,它出现在 [=35= 之前] 前面的字符 21.
尝试使用“?”让你的正则表达式变得非贪婪。修饰符。
tokenDatas.add(new TokenData(Pattern.compile("^(\".*?\":)"), TokenType.IDENTIFIER));
您可能还想允许可选的空格
tokenDatas.add(new TokenData(Pattern.compile("^(\".*?\"\s*:)"), TokenType.IDENTIFIER));
您几乎肯定会遇到与 TokenType.STRING_LITERAL
类似的问题。它也是贪婪的。您可以使用相同的解决方案修复它,即使 .*
非贪婪。
当标记化 JSON 字符串时,它 return 是一个不正确的值,就像它一次连接多个值一样(即 "username": "Azoraqua", "age": }
(它应该是 IDENTIFIER(2 次)和STRING_LITERAL(分别为 1 次),请注意它确实 return age
号作为它自己的标记(分别为 INTEGER_LITERAL)。
我已经尝试了几种方法来实现正确的行为:
- 更改一些与 IDENTIFER 和 STRING_LITERAL.
相关的正则表达式
- 改变一些实际的标记化逻辑。
private static final Set<TokenData> tokenDatas = new LinkedHashSet<>();
static {
tokenDatas.add(new TokenData(Pattern.compile("^(,:)"), TokenType.TOKEN));
tokenDatas.add(new TokenData(Pattern.compile("^(\{)"), TokenType.BEGIN_OBJECT));
tokenDatas.add(new TokenData(Pattern.compile("^(})"), TokenType.END_OBJECT));
tokenDatas.add(new TokenData(Pattern.compile("^(\[)"), TokenType.BEGIN_ARRAY));
tokenDatas.add(new TokenData(Pattern.compile("^(])"), TokenType.END_ARRAY));
tokenDatas.add(new TokenData(Pattern.compile("^(\".*\":)"), TokenType.IDENTIFIER));
tokenDatas.add(new TokenData(Pattern.compile("^(\".*\")"), TokenType.STRING_LITERAL, (s) -> s.substring(1, s.length() - 1)));
tokenDatas.add(new TokenData(Pattern.compile("^((-)?[0-9]+)"), TokenType.INTEGER_LITERAL));
tokenDatas.add(new TokenData(Pattern.compile("^((-)?[0-9]*(\.)[0-9]+)"), TokenType.DOUBLE_LITERAL));
tokenDatas.add(new TokenData(Pattern.compile("^(true|false)", Pattern.CASE_INSENSITIVE), TokenType.BOOLEAN_LITERAL));
}
@Override
public Token next() {
str = str.trim();
if (pushback) {
pushback = false;
return lastToken;
}
if (str.isEmpty()) {
return (lastToken = new Token(TokenType.EMPTY, ""));
}
for (TokenData data: tokenDatas) {
Matcher matcher = data.pattern.matcher(str);
if (matcher.find()) {
String token = matcher.group().trim();
str = matcher.replaceFirst("");
if (data.action != null) {
token = data.action.apply(token);
}
return (lastToken = new Token(data.type, token));
}
}
throw new IllegalStateException("Could not parse " + str);
}
当输入为 {"username": "Azoraqua", "age": 21}
时,输出应为:
1. BEGIN_OBJECT ( {
)
2. 标识符 ( "username":
)
3. STRING_LITERAL ( "Azoraqua"
)
4.令牌(,
)
5.标识符("age"
)
6. INTEGER_LITERAL ( 21
)
7. END_OBJECT ( }
)
我该如何解决这个问题?
问题最有可能出现在这一行:
tokenDatas.add(new TokenData(Pattern.compile("^(\".*\":)"), TokenType.IDENTIFIER));
正则表达式是贪婪的。这意味着他们将尽可能多地匹配。
因此,对于这样的字符串:
"username": "Azoraqua", "age": 21 }
正则表达式的 .*\":
部分将从 "username" 中的 u 开始匹配所有字符,直到并包括最后一个可能的 \":
,它出现在 [=35= 之前] 前面的字符 21.
尝试使用“?”让你的正则表达式变得非贪婪。修饰符。
tokenDatas.add(new TokenData(Pattern.compile("^(\".*?\":)"), TokenType.IDENTIFIER));
您可能还想允许可选的空格
tokenDatas.add(new TokenData(Pattern.compile("^(\".*?\"\s*:)"), TokenType.IDENTIFIER));
您几乎肯定会遇到与 TokenType.STRING_LITERAL
类似的问题。它也是贪婪的。您可以使用相同的解决方案修复它,即使 .*
非贪婪。