在Ruby中将不间断空格转换为空格

28

我遇到了一个问题,有时候从html文本域或输入框中输入的用户数据在编码为utf-8 json时会以\u00a0(不间断空格)而不是空格的形式发送。

我认为这是Firefox的一个BUG,因为我知道用户并没有故意使用不间断空格代替空格。

Ruby也存在两个BUG,其中一个可以用来解决另一个。

由于某种原因,\s无法匹配\u00a0

但是,[^[:print:]](明显不应该匹配)和\xC2\xA0都可以匹配,但我认为这些方法处理该问题不够理想。

是否有其他建议来解决这个问题?


1
哪个Ruby版本?在1.9.2 /\u00a0/中匹配。 - steenslag
\s 不匹配 \u00a0。 \u00a0 在1.9中匹配,但我不确定1.8。 - coolaj86
7
规则#1:当你认为在一个特别受欢迎的程序中发现了一个bug,尤其是在那些经过广泛测试和使用的功能中,例如Firefox的文本区域处理,非常悄声地、仔细地回顾一下你的测试。99次中有98次问题出在你这边。当我在一个文本框中看到出现了不间断空格,而这个文本框很可能被人们用来粘贴文本时,我怀疑是Microsoft Word或者一个设置将“ ”替换为空格的编辑器所导致的。你可以轻松地测试自己的理论,只需要创建一个页面,在其中放置一个文本区域并尝试复制问题即可。 - the Tin Man
6个回答

43

使用/\u00a0/匹配非断行空格。例如,s.gsub(/\u00a0/, ' ')将所有非断行空格转换为常规空格。

使用/[[:space:]]/匹配所有空格,包括Unicode空格(如非断行空格)。这与/\s/不同,后者仅匹配ASCII空格。

参见:Ruby正则表达式文档


查看http://www.unicode.org/versions/Unicode6.2.0/ch06.pdf - 空格字符。但它看起来不完整。 - Andrei Botalov
1
修正了我的答案,只需使用 [[:space]](注意:不是 [:space])。 - Jo Liss
"s.gsub(/\u00a0/, ' ')" 是我一直在寻找的。 - P.M
3
你的回答是正确的,但是你的“个人提醒”缺少尾随冒号。我自己也犯过同样的错误。 - Kelvin

7
如果你不能使用\s匹配Unicode空格,那么这是Ruby正则表达式实现中的一个错误,因为根据UTS#18“Unicode Regular Expressions”Annex C on Compatibility Properties\s绝对必须匹配任何Unicode空格代码点。在\s情况下,标准推荐和POSIX兼容性的两列没有任何余地。你不能通过文档来解决这个问题:如果不这样做,你将违反Unicode标准,特别是UTS#18的RL1.2a
如果您不符合RL1.2a,那么您就不符合Level 1的要求,这是使用Unicode正则表达式所需的最基本和最基础的功能。没有它,您几乎就会迷失方向。这就是为什么有标准存在的原因。 我记得Ruby也未能满足其他几个Level 1的要求。因此,如果您确实需要使用正则表达式处理Unicode,则可能希望使用至少符合Level 1的编程语言。
请注意,您不能使用Unicode General Category属性(如\p{Zs})代替\p{Whitespace}。这是因为Whitespace属性是一个派生属性,而不是一个通用类别。其中还包括控制字符,而不仅仅是分隔符。

4

以下是实际运行的 IRB 代码示例,以回答这个问题。这些示例使用了最新版本的 Ruby(2012年5月)。

Ruby 1.9

require 'rubygems'
require 'nokogiri'
RUBY_DESCRIPTION # => "ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]"
doc = '<html><body> &nbsp; </body></html>'
page = Nokogiri::HTML(doc)
s = page.inner_text
s.each_codepoint {|c| print c, ' ' } #=> 32 160 32
s.strip.each_codepoint {|c| print c, ' ' } #=> 160
s.gsub(/\s+/,'').each_codepoint {|c| print c, ' ' } #=> 160
s.gsub(/\u00A0/,'').strip.empty? #true

Ruby 1.8

require 'rubygems'
require 'nokogiri'
RUBY_DESCRIPTION # => "ruby 1.8.7 (2012-02-08 patchlevel 358) [x86_64-linux]"
doc = '<html><body> &nbsp; </body></html>'
page = Nokogiri::HTML(doc)
s = page.inner_text # " \302\240 "
s.gsub(/\s+/,'') # "\302\240"
s.gsub(/\302\240/,'').strip.empty? #true

2

由于某种原因,\s不能匹配\u00a0。

我认为“某种原因”是因为它本来就不应该匹配。只有POSIX和\p构造字符类才能识别Unicode。字符类缩写则不能:

Sequence   As[...]        Meaning
     \d    [0-9]          ASCII decimal digit character
     \D    [^0-9]         Any character except a digit
     \h    [0-9a-fA-F]    Hexadecimal digit character
     \H    [^0-9a-fA-F]   Any character except a hex digit
     \s    [ \t\r\n\f]    ASCII whitespace character
     \S    [^ \t\r\n\f]   Any character except whitespace
     \w    [A-Za-z0-9\_]  ASCII word character
     \W    [^A-Za-z0-9\_] Any character except a word character

2
哦,它应该这样做的,没错。但它就是不行。请看我的回答。 - tchrist
规范上有写和代码中实际存在是有区别的。无论是否因为规范应该这样,现在都已经没有意义了,因为它并不存在于代码中。无论我们多么希望它出现,除非核心团队中的某个人决定添加它,否则它将不会出现。所以,现实情况是,它不能正常工作,因为它没有被编码。也许在未来的版本中会有所改变。我想看到它符合规范,但他们没有征求我的意见。 - the Tin Man
这是一个非常奇怪的看法。tchrist 是绝对正确的,因为说某个东西“不应该工作”,只因为它当前无法工作,这是我最近读到的最好的空洞真理。无论如何 - 在 [[:space:]] 上使用 gsub,直到有人使 Ruby 实际上符合标准。 - owenmarshall

1

虽然与Ruby(和这个问题)无关,但问题的核心可能是Mac上的Alt+Space会产生一个非断开空格。

这可能会导致各种奇怪的行为(特别是在终端中)。

对于那些对更多细节感兴趣的人,我曾经写过“为什么在Mac OS X中使用管道链接命令不总是有效”关于这个主题。


1

对于旧版本的 Ruby(1.8.x),解决方法如问题所述。

在新版本的 Ruby 1.9+ 中已经修复了此问题。


你能具体一点吗?我在1.9.3p194上遇到了同样的问题,这是相当符合1.9标准的。\s无法匹配Unicode的不间断空格,但\u00a0可以。 - nasmorn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接