【问题标题】:Scanning for Unicode Numbers in a string with \d使用 \d 扫描字符串中的 Unicode 数字
【发布时间】:2011-10-23 08:17:24
【问题描述】:

根据the Oniguruma documentation\d字符类型匹配:

十进制字符
Unicode:General_Category -- Decimal_Number

但是,在包含所有 Decimal_Number 字符的字符串中扫描 \d 会导致仅匹配拉丁文 0-9 位:

#encoding: utf-8
require 'open-uri'
html = open("http://www.fileformat.info/info/unicode/category/Nd/list.htm").read
digits = html.scan(/U\+([\da-f]{4})/i).flatten.map{ |s| s.to_i(16) }.pack('U*')

puts digits.encoding, digits
#=> UTF-8
#=> 0123456789٠١٢٣٤٥٦٧٨٩۰۱۲۳۴۵۶۷۸۹߀߁߂߃߄߅߆߇߈߉०१२३४५६७८९০১২৩৪৫৬৭৮৯੦੧੨…

p RUBY_DESCRIPTION, digits.scan(/\d/)
#=> "ruby 1.9.2p180 (2011-02-18) [i386-mingw32]"
#=> ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]

我是否误读了文档?为什么\d 不匹配其他 Unicode 数字,和/或有办法让它匹配?

【问题讨论】:

  • 是的,Ruby 正则表达式在 Unicode 方面有很多非常烦人的问题。请参阅我最近的 OSCON Unicode Support Shootout 演讲中关于 Ruby 的幻灯片 15-20,尤其是最后一张。另一方面,它确实进行了完全折叠,这是除了 Perl 之外唯一这样做的引擎。但是由于 Ruby 不满足 UTS#18’s Level 1 conformance requirements 的最基本的 Unicode 正则表达式功能,所以你很不走运。恐怕你需要使用 ICU 或 Perl 来完成真正的 Unicode 工作。

标签: ruby regex unicode character-properties


【解决方案1】:

Brian Candler on ruby-talk注意到:

  • \w 只匹配 ASCII 字母和数字,而[[:alpha:]] 匹配全套 Unicode 字母。
  • \d 仅匹配 ASCII 数字,而 [[:digit:]] 匹配完整的 Unicode 数字集。

因此行为是“一致的”,我们有一个针对 Unicode 数字的简单解决方法。在the same Oniguruma doc 中阅读\w,我们看到了文字:

\w  word character  
    Not Unicode: alphanumeric, "_" and multibyte char.  
    Unicode: General_Category -- (Letter|Mark|Number|Connector_Punctuation)

鉴于 Ruby 的实际行为和上面的“非 Unicode”文本,文档似乎描述了两种模式——Unicode 模式和非 Unicode 模式——并且 Ruby 正在非 Unicode 模式下运行.

这可以解释为什么 \d 不匹配完整的 Unicode 集:尽管 Oniguruma 文档未能准确描述在非 Unicode 模式下匹配的内容,但我们现在知道记录为“Unicode”的行为不应该是预计。

p "abç".scan(/\w/), "abç".scan(/[[:alpha:]]/)
#=> ["a", "b"]
#=> ["a", "b", "\u00E7"]

留给读者作为练习来了解如何(如果有的话)在 Ruby 正则表达式中启用 Unicode 模式,因为 /u 标志(例如 /\w/u)不会这样做。 (也许 Ruby 必须使用 Oniguruma 的特殊标志重新编译。)

更新:看来我链接到的 Oniguruma 文档对于 Ruby 1.9 并不准确。请参阅this ticket discussion,包括这些帖子:

[Yui NARUSE] “RE.txt 用于原始 Oniguruma,而不用于 Ruby 1.9 的正则表达式。我们可能需要自己的文档。”
[Matz] “我们的 Oniguruma 是分叉的。在 geocities.jp 中找到的原始 Oniguruma 没有改变。”

更好的参考:这里是关于 Ruby 1.9 正则表达式语法的官方文档:
https://github.com/ruby/ruby/blob/trunk/doc/re.rdoc

【讨论】:

  • \w 与拉丁字母或数字不匹配。它与 ASCII 匹配。拉丁语!= ASCII。耻辱耻辱。我敢让你尝试使用 Ruby 的微不足道的属性来模拟 \w 的 UTS#18 版本:这是不可能的。此外,所有 POSIX 属性都不符合规范。有些人甚至撒谎。例如,alpha 不应该表示字母!因此,它很烂。
  • @tchrist 感谢您提供正确的术语;我将编辑我的答案以匹配。我谦虚地建议,Ruby 对 Unicode 'sucks' 的处理是否不是 Stack Overflow 讨论的合适主题。此外,假设您基于不正确的措辞对我的回答投了反对票,我要求您再次阅读该问题,并根据我所写的内容是否正确回答了该问题来投票。
  • 问题是它不符合标准。它使用标准所说的东西做一件事,但它以另一种方式使用它们。这是非常具有误导性的。例如,Ⓜ 是一个 (other_) 字母字符和一个单词字符,但 Ruby 不能这样匹配它,因为它是一个符号。这是一个错误。见悉尼科技大学#18。还有更严重的问题。
  • @tchrist 我并不是说你错了,只是你所讨论的与这个问题完全无关。您似乎在使用这个问题来发泄您对 Ruby 对 Unicode 处理不当(仅限于或不限于正则表达式)的不满。最后:"Ⓜ".scan(/[[:alpha:]]/) 的工作原理正如我相信你所说的那样。
  • 你说得对:抽查显示比我上次看到的更多。不知道之前的运行发生了什么。奇怪。
【解决方案2】:

改用 Unicode 字符类 \p{N}。这匹配所有 Unicode 数字。不知道为什么 \d 不起作用。

【讨论】:

  • 您的\pN(真的,\p{GC=Number})匹配的不仅仅是所有数字:它匹配所有数字,包括非数字。只是数字是\p{Nd}(真的,\p{GC=Decimal_Number}\p{Numeric_Type=Decimal} 完整的Unicode)。 Unicode 中的所有数字都有数值,但并非所有数字都是数字。数字是以 10 为底的 bigendian 数字,您可以从中构建更大的数字。数字七、上标七和罗马数字七都是\pN\p{NV=7}。但是,它们的一般类别是Decimal_NumberOther_NumberLetter_Number唉,Ruby 最多只支持 3 个 Unicode 属性!
  • 更正:Ruby 不支持三个 Unicode 属性;它只支持两个 Unicode 属性,General_CategoryScript 属性。由于 Ruby strings-keelp-their-encoding design-bug,即使是那些也不能在所有字符串上工作。它有一些你不能信任的虚假属性,因为它们与 Unicode 标准中的名称相同,但行为却相反。其中之一是 alphabetic 属性,它在 Ruby 中已损坏。伤心伤心伤心。
【解决方案3】:

\d 默认只匹配 ASCII 数字。您可以使用(反直觉的)(?u) 语法在正则表达式中手动打开 Unicode 匹配:

"?".match(/(?u)\d/) # => #<MatchData "?">

或者,您可以在您的正则表达式中使用“posix”或“unicode property”样式,这不需要您手动打开 Unicode 匹配:

/[[:digit:]]/ # posix style
/\p{Nd}/ # unicode property/category style

您可以在这篇博文中找到有关如何在 Ruby 中对 Unicode 字符进行高级匹配的更多详细信息: https://idiosyncratic-ruby.com/30-regex-with-class.html

【讨论】:

    猜你喜欢
    • 2017-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-02
    • 1970-01-01
    • 2012-10-14
    • 1970-01-01
    相关资源
    最近更新 更多