java - 为什么这个分词器返回不正确的值?
问题描述
当标记一个 JSON 字符串时,它返回一个不正确的值,就像它一次连接多个值(即"username": "Azoraqua", "age": }
(它应该分别是 IDENTIFIER(2 次)和 STRING_LITERAL(1 次)),请注意它确实返回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. IDENTIFIER ( "username":
)
3. STRING_LITERAL ( "Azoraqua"
)
4. TOKEN ( ,
)
5. IDENTIFIER ( "age"
)
6. INTEGER_LITERAL ( 21
)
7. END_OBJECT ( }
)
我该如何解决这个问题?
解决方案
问题很可能出在这一行:
tokenDatas.add(new TokenData(Pattern.compile("^(\".*\":)"), TokenType.IDENTIFIER));
正则表达式是贪婪的。这意味着他们将尽可能多地匹配。
因此,对于这样的字符串:
"username": "Azoraqua", "age": 21 }
.*\":
正则表达式的部分将从“用户名”中的 u 开始匹配所有字符,包括最后一个可能\":
出现的字符,该字符出现在 21 前面的“空格”字符之前。
尝试使用“?”使您的正则表达式不贪婪。修饰符。
tokenDatas.add(new TokenData(Pattern.compile("^(\".*?\":)"), TokenType.IDENTIFIER));
您可能还希望允许可选的空格
tokenDatas.add(new TokenData(Pattern.compile("^(\".*?\"\s*:)"), TokenType.IDENTIFIER));
您几乎肯定会遇到类似的问题TokenType.STRING_LITERAL
。它也是贪婪的。您可以使用相同的解决方案修复它,即使.*
非贪婪。
推荐阅读
- ios - 更改模态视图的背景颜色
- java - 如何定义本地关键 GemFire 区域的持久性?
- php - PHP preg_replace - 从句子中替换各种 URL 的所有实例
- robotframework - 如何根据运行机器人脚本的浏览器更改关键字中使用的变量?
- javascript - 将 MVC 模型数组传递到 javascript 数组的问题
- kotlin - 仅将标记为 reified 的类型传递给 Kotlin 泛型函数
- r - 计算 90% 并用 R 中的组中位数替换它
- sql - 从一组记录中重新插入失败的记录
- python - 在 R/Python 中分析巨大的 csv 文件并根据文件的分布采样 X%?
- android - hprof 查看器中的 $classOverhead 是什么意思?