正则表达式“punct”字符类根据Ruby版本匹配不同的字符

时间:2017-02-06 14:30:53

标签: ruby regex posix

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?所示)。

1 个答案:

答案 0 :(得分:3)

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

正确的行为是匹配所有标点符号,如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}/