正则表达式 "punct" 字符 class 根据 Ruby 版本匹配不同的字符

Regex "punct" character class matches different characters depending on Ruby version

Ruby的字符类为标点字符,即[:punct:]\p{Punct}\p{P} 似乎匹配不同的字符,具体取决于我使用的 Ruby 版本。

这是一个小例子:(很抱歉弄乱了 SO 的语法高亮显示)

# punct.rb
chars = <<-EOD.split
! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ ] ^ _ ` { | } ~
EOD

matched, not_matched = chars.partition { |c| c =~ /[[:punct:]]/ }

puts "    matched: #{matched.join}"
puts "not matched: #{not_matched.join}"

使用 Ruby 1.9.3 并再次使用 Ruby 2.4.0 我得到:

    matched: !"#$%&'()*+,-./:;<=>?@[]^_`{|}~
not matched:

但是中间的各种 Ruby 版本 (2.0.x, 2.1.x, 2.2.x, 2.3.x) 给出我:

    matched: !"#%&'()*,-./:;?@[]_{}
not matched: $+<=>^`|~

为什么会这样,什么是正确的行为?更重要的是:如何在 Ruby 个版本中获得一致的结果?

尝试更改我的语言环境无济于事(如 Why does Ruby /[[:punct:]]/ miss some punctuation characters? 所建议)。

Ruby 1.9.3 使用 US_ASCII 作为默认编码,正确匹配所有标点符号。 Ruby 2.0 将其默认编码切换为 UTF-8,引入了您发现的错误,导致标点符号匹配不当。 Ruby 2.4 修复了这个错误。

正确的行为是匹配所有标点符号,如 ruby 1.9.3 和 2.4 所做的那样。这与标点符号的 POSIX 正则表达式定义一致。

使您的代码保持一致的一种选择是将所有字符串编码为 US_ASCII 或没有 UTF-8 错误的替代方法:

matched, unmatched = chars.partition { |c| c.encode(Encoding::US_ASCII) =~ /[[:punct:]]/ }

但这可能并不理想,因为它会强制您对字符串使用限制性编码。

另一个选项是手动定义标点符号:

/[!"\#$%&'()*+,\-./:;<=>?@\[\\]^_`{|}~]/

它有点不雅观,但您可以将其放入变量中,然后以这种方式将其添加到正则表达式中:

punctuation = "[!\"\#$%&'()*+,\-./:;<=>?@\[\\]^_`{|}~]"
my_regex = /#{punctuation}/