【问题标题】:Java regex working differently on Android than in JavaJava 正则表达式在 Android 上的工作方式与在 Java 上的工作方式不同
【发布时间】:2015-05-12 08:05:39
【问题描述】:

我在 Android 上使用 Java 正则表达式,但我看到了奇怪的差异,如下所示

Java:"COSÌ".replaceAll( "\\W", "" ) ----> "COS"

安卓:"COSÌ".replaceAll( "\\W", "" ) ----> "COSÌ"

有人注意到 Java 和 Android String 类之间的相似差异吗?

【问题讨论】:

  • 检查你的 Java 版本:android 运行 Java 6 或 Java 7。
  • @TheLostMind:Android 使用 ICU 正则表达式,我上次查看文档时。 ICU 和 Java 非常相似,但又不一样。
  • @nhahtdh- 哦.. Android 中使用的正则表达式实现由 ICU 提供。正则表达式的符号主要是其他 Java 语言实现中使用的符号的超集。这意味着现有应用程序将正常工作,但在极少数情况下,Android 可能会接受其他实现不接受的正则表达式。 好吧,这可能是那些极少数情况之一 :)

标签: java android regex string


【解决方案1】:

安卓

直接来自the Android documentation,紧随简写字符类列表(\d\w\s 等)之后:

请注意,这些内置类不仅仅涵盖传统的 ASCII 范围。例如,\w 等价于字符类[\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nd}]

这也可以解释为什么 Ì 不会被 Android 版本上运行的相同代码替换。

虽然简写字符类也匹配 Unicode 字符是正确的,但\w Android 文档的示例定义方式已过时。有关详细信息,请参阅附录。

Java SE

相比之下,在 Java SE 中,默认情况下,\w 等价于 [a-zA-Z_0-9]

\w 仅在指定 Pattern.UNICODE_CHARACTER_CLASS 标志时匹配 Unicode 字字符。指定标志时:

  • 在 Java 7 中,\w[\p{IsAlphabetic}\p{M}\p{Nd}\p{Pc}] 具有相同的定义
  • 在 Java 8 中,\w 更新为 [\p{IsAlphabetic}\p{M}\p{Nd}\p{Pc}\u200c\u200d]

解决方法

直接指定字符类。 ICU 正则表达式不支持 ASCII 字符类:

[^a-zA-Z0-9_]

附录

ICU 中\w 的定义

以下是\w 随时间演变的过程:

  • 简写字符类 \w 被定义为 [\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nd}](如文档中所示)直到 ICU 3.0

  • 使用从ICU 3.2(2006/02/24 发布)到ICU 4.8.1.1[\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}](相当于源代码中的[\p{Alphabetic}\p{M}\p{Nd}\p{Pc}])代替。更改于revision 16634

  • ICU 49(2012/06/06 发布)开始,文档中的当前定义使用[\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\u200c\u200d](相当于源代码中的[\p{Alphabetic}\p{M}\p{Nd}\p{Pc}\u200c\u200d])。更改为 revision 31278

上面的字符串用于构造URX_ISWORD_SET,在doBackslashW中的regcmp.cpp中使用,用于编译正则表达式。

安卓使用的ICU版本

即使在 android-1.6_r1(甜甜圈),当 Pattern 类文档贫瘠时,它已经在使用 ICU 3.8。 The source code 表明它正在使用第二个要点中的定义。

文档可能回退到描述最旧版本 Android 的行为。

参考

如果您想自己浏览 Android 的源代码:

  • libcore(Java 类库)

    • android-1.6_r1android-2.2.3_r2.1platform/dalvik 存储库。 Pattern 类可以位于libcore/regex/src/main/java/java/util/regex/Pattern.java
    • android-2.3_r1 到现在,platform/libcore 存储库。 Pattern 类可以位于/luni/src/main/java/java/util/regex/Pattern.java
  • icu4c(C 的 ICU 库)

    • android-1.6_r1android-4.4.4_r2.0.1platform/external/icu4c 存储库。正则表达式相关的东西可以在i18n找到,Unicode相关的东西可以在common找到。
    • android-5.0.0_r1 到现在,platform/external/icu。输入icu4c/source,然后输入与上述类似的路径。

【讨论】:

    【解决方案2】:

    看看Android Regular expression syntax documentation:

    请注意,这些内置类不仅仅涵盖传统的 ASCII 范围。例如,\w 等价于字符类 [\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nd}]。更多详情见Unicode TR-18, 请记住,每个类别中的字符集可能会有所不同 Unicode 版本之间。如果您实际上只想匹配 ASCII 字符,指定你想要的显式字符;如果你的意思是 0-9 使用[0-9] 而不是\d,这也将包括Gurmukhi 数字和 以此类推。

    因此,使用范围来确保您只匹配英文字母replaceAll("[^a-zA-Z0-9_]", "")

    【讨论】:

    • 如果你想在Java SE中不区分大小写地匹配Unicode,UNICODE_CASE必须和CASE_INSENSITIVE标志一起使用;否则,只有 ASCII 范围字符不区分大小写。这与字符类\w 的行为方式无关。
    • @nhahtdh:您因为解释而否决了我的答案并提供相同的解决方案?在这种情况下,我只会发表评论而不回答。
    • 解决方案是相同的(它是非常基本的正则表达式),但解释完全不同(在您编辑之前),我将拒绝提供错误的解释。
    • @nhahtdh:我真的很喜欢你的一致性,而我的一些反对者通常缺乏这种一致性。感谢您的健康批评。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-14
    • 1970-01-01
    • 2015-04-30
    • 2016-08-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-09
    相关资源
    最近更新 更多