11

我遇到了以下问题(简化)。我写了以下

Pattern pattern = Pattern.compile("Fig.*");
String s = readMyString();
Matcher matcher = pattern.matcher(s);

在读取一个字符串时,即使它以“Fig”开头,匹配器也无法匹配。我将问题追溯到字符串下一部分中的流氓字符。它的 codePoint 值 1633 来自

(int) charAt(i)

但与正则表达式不匹配。我认为这是由于输入过程中某处的非 UTF-8 编码所致。

Javadocs说:

预定义的字符类。任何字符(可能匹配也可能不匹配行终止符)

大概这不是严格意义上的字符,但仍然是字符串的一部分。如何检测此问题?

更新:这是由于 (char)10 不容易发现。我上面的诊断是错误的,下面的所有答案都与所提出的问题相关并且很有用。

4

4 回答 4

13

检查这一点很容易:

import java.util.regex.*;

public class Test {
    public static void main(String[] args) {
        Pattern pattern = Pattern.compile(".");
        for (char c = 0; c < 0xffff; c++) {
            String text = String.valueOf(c);
            if (!pattern.matcher(text).matches()) {
                System.out.println((int) c);
            }
        }
    }
}

在我的盒子上,输出是:

10
13
133
8232
8233

其中,10 和 13 分别是“\n”和“\r”。133(U+0085)是“下一行”,8232(U+2028)是“行分隔符”,8233(U+2029)是“段落分隔符”。

注意:

  • 这不会测试基本多语言平面之外的任何 Unicode 字符
  • 它只使用默认选项
  • 这似乎与您对角色 1633 (U+0661) 的体验相矛盾
于 2013-04-22T15:01:34.720 回答
11

Java 正则表达式中的.字符匹配行终止符之外的任何字符,除非您Pattern.DOTALL在编译模式时使用该标志。

为此,您将使用这样的模式:

Pattern p = Pattern.compile("somepattern", Pattern.DOTALL);
于 2013-04-22T15:01:10.770 回答
2

根据文档,根据标志.可以有3 种略有不同的解释。

默认

.当mode 和mode 被禁用时,将排除行终止符(默认):DOTALLUNIX_LINES

行终止符是一个或两个字符的序列,用于标记输入字符序列的一行的结尾。以下被认为是线路终止符:

  • 换行符(换行符)('\n'),
  • 一个回车符,后跟一个换行符 ( "\r\n"),
  • 一个独立的回车符 ( '\r'),
  • 下一行字符 ( '\u0085'),
  • 行分隔符 ( '\u2028'),或
  • 段落分隔符 ( '\u2029')。

这意味着.相当于[^\n\r\u0085\u2028\u2029]在这种情况下。

UNIX_LINES启用模式但DOTALL禁用模式时

. \nUNIX_LINES启用模式但DOTALL禁用模式时才会排除。这意味着.相当于[^\n]在这种情况下。

如果UNIX_LINES激活模式,则唯一识别的行终止符是换行符。

DOTALL启用模式时

如果DOTALL启用模式,.将匹配任何字符,无异常

除非指定了标志,否则正则表达式.匹配除行终止符之外的任何字符。DOTALL

于 2013-04-22T15:24:12.413 回答
1

关于使用正则表达式非打印字符,您可以阅读以下两篇文章:

  1. 如何使用正则表达式匹配不可打印的字符
  2. 如何在正则表达式中使用 Unicode 代码点、属性、块和脚本

即使您完全使用 UTF,也会有很多惊喜。

于 2013-04-22T15:07:54.727 回答