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

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来逐个匹配并消费输入内容,这是更适合这种无分隔符、按正则从头匹配场景的工具。

通用实现思路

  1. 把整个输入读取成字符串(大文件可分段处理,核心逻辑一致)。
  2. 依次用各个目标正则模式去匹配当前剩余的字符串头部。
  3. 每次匹配到一个模式后,记录对应的令牌类型,然后更新当前匹配位置到匹配结束处。
  4. 重复这个过程直到整个输入被处理完毕。

代码示例

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 02:30:56