Java中如何扫描无分隔符文件并按指定模式提取令牌?
问题描述
我有如下格式的输入文本:
!10#6#4!3#4
针对输入里的两类数据,我定义了两个正则模式:
Pattern totalPattern = Pattern.compile("![0-9]+"); Pattern valuePattern = Pattern.compile("#[0-9]+");
期望得到的输出是:
total value value total value
我写了如下代码尝试实现:
try (Scanner scanner = new Scanner(inputFile)) { while (scanner.hasNext()) { if (scanner.hasNext(totalPattern)) { System.out.print("total "); scanner.next(totalPattern); } else if (scanner.hasNext(valuePattern)) { System.out.print("value "); scanner.next(valuePattern); } } }
但这段代码无法正常工作——因为输入没有分隔符,Scanner会把整个输入当成单个令牌。我不能修改输入格式,尝试修改Scanner的分隔符也没成功,觉得Scanner可能不是合适的工具,但不知道该用什么替代方案。
补充说明1
有人提到可以把!和#作为分隔符,但如果输入变成这样:
!!!10#6###4!!3#4
对应的正则模式改为:
Pattern totalPattern = Pattern.compile("[!]+[0-9]+"); Pattern valuePattern = Pattern.compile("[#]+[0-9]+");
这时如果需要把!或#的数量作为数据信息,用它们做分隔符就不可行了,还是需要合适的替代工具。
补充说明2
我之前的问题描述不够准确,实际需要的是通用解决方案:接收一组正则模式,从输入头部开始匹配并消费内容,返回拆分后的令牌序列,上面的两种模式只是示例。
解决方案
不要用Scanner,改用Matcher来逐个匹配并消费输入内容,这是更适合这种无分隔符、按正则从头匹配场景的工具。
通用实现思路
- 把整个输入读取成字符串(大文件可分段处理,核心逻辑一致)。
- 依次用各个目标正则模式去匹配当前剩余的字符串头部。
- 每次匹配到一个模式后,记录对应的令牌类型,然后更新当前匹配位置到匹配结束处。
- 重复这个过程直到整个输入被处理完毕。
代码示例
import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Tokenizer { // 绑定令牌类型与对应正则模式 static class TokenPattern { String type; Pattern pattern; TokenPattern(String type, Pattern pattern) { this.type = type; this.pattern = pattern; } } public static List<String> tokenize(String input, List<TokenPattern> tokenPatterns) { List<String> tokens = new ArrayList<>(); int currentPosition = 0; int inputLength = input.length(); while (currentPosition < inputLength) { boolean matched = false; // 遍历所有正则模式尝试匹配 for (TokenPattern tp : tokenPatterns) { Matcher matcher = tp.pattern.matcher(input); // 限定当前匹配范围 matcher.region(currentPosition, inputLength); if (matcher.lookingAt()) { // 仅匹配当前位置开始的头部内容 tokens.add(tp.type); currentPosition = matcher.end(); // 更新位置到匹配结束处 matched = true; break; } } if (!matched) { // 处理无法匹配的异常情况 throw new IllegalArgumentException("无法匹配输入的位置:" + currentPosition); } } return tokens; } public static void main(String[] args) { // 测试示例1 String input1 = "!10#6#4!3#4"; List<TokenPattern> patterns1 = List.of( new TokenPattern("total", Pattern.compile("![0-9]+")), new TokenPattern("value", Pattern.compile("#[0-9]+")) ); List<String> tokens1 = tokenize(input1, patterns1); System.out.println(String.join(" ", tokens1)); // 输出:total value value total value // 测试示例2 String input2 = "!!!10#6###4!!3#4"; List<TokenPattern> patterns2 = List.of( new TokenPattern("total", Pattern.compile("[!]+[0-9]+")), new TokenPattern("value", Pattern.compile("[#]+[0-9]+")) ); List<String> tokens2 = tokenize(input2, patterns2); System.out.println(String.join(" ", tokens2)); // 输出:total value value total value } }
代码说明
lookingAt()方法:与matches()不同,它仅匹配输入字符串的开头部分,正好满足从头部逐步消费的需求。region()方法:用来限定Matcher的匹配范围,每次匹配后更新起始位置,实现逐步处理输入的效果。- 通用结构:通过
TokenPattern类绑定令牌类型与正则,支持传入任意数量的模式,完全符合通用解决方案的要求。
内容的提问来源于stack exchange,提问作者redmoncoreyl
相关产品推荐
相关产品推荐

