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?所示)。
答案 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}/