使用 \R 模式的 Java 扫描程序(缓冲区边界问题)

2022-09-04 04:49:24

摘要:Java中(或其他正则表达式模式)的使用是否有任何警告/已知问题(特别是关于内部缓冲区的边界条件)?\RScanner

详:由于我想对潜在的多平台输入文件进行一些多行模式匹配,因此我将模式与 一起使用,根据javadoc,它是:\RPattern

任何 Unicode 换行序列 等效于\u000D\u000A|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029]

无论如何,我注意到在我的一个测试文件中,应该解析十六进制转储块的循环被缩短了。经过一些调试,我注意到它结束的行是Scanger内部缓冲区的末尾。

这是我为模拟这种情况而编写的测试程序:

public static void main(String[] args) throws IOException {
    testString(1);
    testString(1022);
}

private static void testString(int prefixLen) {
    String suffix = "b\r\nX";
    String buffer = new String(new char[prefixLen]).replace("\0", "a") + suffix;

    Scanner scanner = new Scanner(buffer);
    String pattern = "b\\R";
    System.out.printf("=================\nTest String (Len=%d): '%s'\n'%s' found with horizon=0 (w/o bound): %s\n", buffer.length(), convertLineEndings(
        buffer), pattern, convertLineEndings(scanner.findWithinHorizon(pattern, 0)));
    System.out.printf("'X' found with horizon=1: %b\n", scanner.findWithinHorizon("X", 1) != null);
    scanner.close();
}

private static String convertLineEndings(String string) {
    return string.replaceAll("\\n", "\\\\n").replaceAll("\\r", "\\\\r");
}

...它产生以下输出(为格式/简洁而编辑):

=================
Test String (Len=5): 'ab\r\nX'
'b\R' found with horizon=0 (w/o bound): b\r\n
'X' found with horizon=1: true
=================
Test String (Len=1026): 'a ... ab\r\nX'
'b\R' found with horizon=0 (w/o bound): b\r
'X' found with horizon=1: false

对我来说,这看起来像一个错误!我认为扫描仪应该以相同的方式将其与模式相匹配,而与它们在输入文本中的显示位置无关(只要不涉及模式)。(我还发现了可能相关的开放JDK bugs 81764078072582,但这是常规的Oracle JDK 8u111)。suffixprefix

但是我可能错过了一些关于扫描程序或特定模式用法的建议(或者Open JDK和Oracle在这里为相关类提供了相同的(??)实现?...因此,问题来了!\R


答案 1

我在Ideone上测试了这段代码,它在最新版本的Java上不再返回“false”。

https://www.ideone.com/4wwYSj

但是,如果我被困在旧版本或仍然显示该错误的版本上,并且我需要一个通用的解决方案而不是这个例子的解决方法,那么我可能会尝试制作一个类似于正则表达式,但这会强制在案例中进行额外的字节窥视。请注意,文档中所谓的“等效”模式并不是真正的等效模式,因为它实际上需要是原子分组。所以你最终可能会得到这样的东西:\R\r

(?>\u000D\u000A|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029](?=.|\Z))


答案 2

两个建议:

我认为你应该以这种方式测试X:

System.out.printf("'X' found with horizon=1: %b\n", 
    scanner.findWithinHorizon("X", prefixLen) != null);

(由于除 0 以外的任何内容作为 horizon 参数都会将搜索限制为一定数量的字符。这已经在方法的名称中。地平线是该方法所看到的。

可能您的文件编码有问题。您的扫描仪可能选择了错误的默认编码。尝试一些类似的东西:

new Scanner(file, "utf-8");