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); } }
补充说明
- 正则表达式本身是没问题的,
.*!include (?<fileName>.*\\.(yaml|yml))确实能匹配整行包含!include加yaml/yml文件名的内容。 - 使用
String.join()代替reduce拼接字符串,代码更简洁且性能更好。 - 用
System.lineSeparator()代替硬编码的\r\n,能适配Windows、Linux等不同系统的换行规则。
内容的提问来源于stack exchange,提问作者floydk
相关产品推荐
相关产品推荐

