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

String.matches与Pattern.matcher.hasMatch结果不一致问题排查

问题

我正在编写一个小型Java库,需求是逐行读取文件,当某行包含!include .yaml表达式时,提取文件名、加载对应文件内容并替换当前行。

示例order文件内容:

orderNo: A001
date: 2019-04-17
customerName: Customer, Joe
orderLines:
  - item: No. 9 Sprockets
    quantity: 12
    unitPrice: 1.23
  - item: Widget (10mm)
    quantity: 4
    unitPrice: 3.45
  - !include item.yaml

希望将最后一行替换为item.yaml的内容:

item: Sub-item
quantity: 2
unitPrice: 16

我用正则表达式匹配整行!include .yaml,代码如下:

private static final Pattern includePattern = Pattern.compile(".*!include (?<fileName>.*\\.(yaml|yml))");
public static String readConfiguration(String configurationFile) {
    final var configurationFileUrl = ConfigurationLoader.class.getClassLoader().getResource(configurationFile);
    if (configurationFileUrl == null)
        throw new ConfigurationReadException(configurationFile);
    try {
        final var lines = Files.readAllLines(Path.of(configurationFileUrl.toURI()));
        for (final var line : lines) {
            final var matcher = includePattern.matcher(line);
            if (matcher.hasMatch()) {
                final var includedFileName = matcher.group("fileName");
                final var includedConfiguration = readConfiguration(includedFileName);
            }
        }
        return lines.stream().reduce("", (existing, current) -> existing.equals("") ? current : "%s\r\n%s".formatted(existing, current));
    } catch (IOException | OutOfMemoryError | SecurityException | IllegalArgumentException |
             FileSystemNotFoundException | URISyntaxException e) {
        throw new ConfigurationReadException(configurationFile.toString(), e);
    }
}

现在遇到的问题:matcher.hasMatch()始终返回false,无法匹配到分组,但调试时执行line.matches(includePattern.pattern())却返回true。我理解matches()要求匹配整个字符串,因此已调整正则以匹配整行,在线正则测试也显示正则有效,请问哪里出错了?

解答

核心错误:方法名拼写错误

Java的Matcher类里没有hasMatch()方法,你要调用的是matches()方法——这个方法会判断整个输入字符串是否匹配正则表达式,和String.matches()的行为完全一致。你误写了方法名,导致条件永远不成立。

额外问题:未实现内容替换

即使修正了方法名,当前代码也只是递归读取了包含的文件,但没有把原行替换为读取到的内容。因为Files.readAllLines()返回的是不可变的List<String>,你需要改用可变集合(比如ArrayList)来处理替换逻辑。

修正后的代码示例

private static final Pattern includePattern = Pattern.compile(".*!include (?<fileName>.*\\.(yaml|yml))");

public static String readConfiguration(String configurationFile) {
    final var configurationFileUrl = ConfigurationLoader.class.getClassLoader().getResource(configurationFile);
    if (configurationFileUrl == null)
        throw new ConfigurationReadException(configurationFile);
    try {
        // 改为可变ArrayList,方便替换行内容
        final var lines = new ArrayList<>(Files.readAllLines(Path.of(configurationFileUrl.toURI())));
        for (int i = 0; i < lines.size(); i++) {
            final var line = lines.get(i);
            final var matcher = includePattern.matcher(line);
            if (matcher.matches()) {
                final var includedFileName = matcher.group("fileName");
                final var includedConfiguration = readConfiguration(includedFileName);
                // 替换当前行为包含文件的内容
                lines.set(i, includedConfiguration);
            }
        }
        // 用System.lineSeparator()适配不同系统的换行符,比硬编码\r\n更通用
        return String.join(System.lineSeparator(), lines);
    } catch (IOException | OutOfMemoryError | SecurityException | IllegalArgumentException |
             FileSystemNotFoundException | URISyntaxException e) {
        throw new ConfigurationReadException(configurationFile.toString(), e);
    }
}

补充说明

  1. 正则表达式本身是没问题的,.*!include (?<fileName>.*\\.(yaml|yml))确实能匹配整行包含!include加yaml/yml文件名的内容。
  2. 使用String.join()代替reduce拼接字符串,代码更简洁且性能更好。
  3. 用System.lineSeparator()代替硬编码的\r\n,能适配Windows、Linux等不同系统的换行规则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 12:35:23