You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让java.util.Scanner将标点分隔符作为令牌返回以解析算术表达式

解决Java Scanner对算术表达式的分词问题

这个需求我之前也碰到过,Scanner的分隔符逻辑有时候确实容易绕晕,咱们一步步理清楚问题,再给出可行的解决方案:

先分析你之前两种尝试的问题

  • 第一种方法(吞掉标点):useDelimiter("\\p{Space}+|\\p{Punct}")
    这里你把标点符号也设成了分隔符,Scanner会直接丢弃所有分隔符,所以标点自然就消失了;另外当标点和其他字符紧密相连时(比如(ab里的(),分隔符会在(和ab之间拆分,但因为(本身是分隔符,拆分后就会产生空字符串,这就是结果里出现""的原因。

  • 第二种方法(部分令牌粘连):useDelimiter("\\p{Space}+|(?=\\p{Punct})")
    你用了正向预查(?=\\p{Punct}),这个规则只会在标点符号前面的位置触发拆分。比如ab和-之间会拆分,但-和bc不会——因为-后面没有预查规则,所以-会和bc粘在一起;同理(和ab也会粘在一起,因为(前面没有触发拆分的位置。

两种可行的解决方案

方案1:调整分隔符,结合正向+反向预查

我们需要让Scanner在标点符号的前后都触发拆分,这样既能保留标点作为独立令牌,又能丢弃空格。可以用以下正则作为分隔符:

List<String> tokens = new Scanner("12 + (ab-bc*3)")
    .useDelimiter("\\p{Space}+|(?<=\\p{Punct})|(?=\\p{Punct})")
    .tokens()
    .filter(token -> !token.isEmpty()) // 过滤分隔符相邻产生的空字符串
    .collect(Collectors.toList());

正则规则解释:

  • \\p{Space}+:匹配任意数量的空格,作为分隔符直接丢弃
  • (?<=\\p{Punct}):反向预查——匹配一个位置,这个位置的前一个字符是标点,这样标点后面的位置会被当作拆分点,标点就会被单独拆成一个令牌
  • (?=\\p{Punct}):正向预查——匹配一个位置,这个位置的后一个字符是标点,这样标点前面的内容会被拆成一个令牌

运行后得到的结果完全符合你的预期:["12", "+", "(", "ab", "-", "bc", "*", "3", ")"]

方案2:直接用findAll匹配目标令牌(更推荐)

其实Scanner的findAll方法可以直接定义你想要的令牌类型,比调整分隔符逻辑更直观。我们只需要匹配两种令牌:

  1. 单个标点符号:\\p{Punct}
  2. 连续的非标点、非空格字符:[^\\p{Punct}\\p{Space}]+

代码如下:

List<String> tokens = new Scanner("12 + (ab-bc*3)")
    .findAll("\\p{Punct}|[^\\p{Punct}\\p{Space}]+")
    .map(MatchResult::group)
    .collect(Collectors.toList());

这种方法不需要处理空字符串,因为findAll只会返回符合规则的匹配项,结果和方案1完全一致,逻辑也更清晰——直接告诉程序“我要找的就是单个标点,或者一串非标点非空格的字符”。

内容的提问来源于stack exchange,提问作者Danilo Piazzalunga

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:29:21