【问题标题】:Does multibyte character interfere with end-line character within a regex?多字节字符是否会干扰正则表达式中的结束行字符?
【发布时间】:2013-03-24 16:14:49
【问题描述】:

使用这个正则表达式:

regex1 = /\z/

以下字符串匹配:

"hello" =~ regex1 # => 5
"こんにちは" =~ regex1 # => 5

但使用这些正则表达式:

regex2 = /#$/?\z/
regex3 = /\n?\z/

他们表现出不同:

"hello" =~ regex2 # => 5
"hello" =~ regex3 # => 5
"こんにちは" =~ regex2 # => nil
"こんにちは" =~ regex3 # => nil

什么是干扰?字符串编码为 UTF-8,操作系统为 Linux(即$/ 为"\n")。多字节字符是否干扰$/?怎么样?

【问题讨论】:

  • 仅供参考:/[[:space:]]?\z/ 有效。
  • 有趣的是,"こんにちは" =~ /\n?$/ 说5。在 2.0 和 1.9 的两种情况下,我也看到了相同的行为。
  • 不是 UTF-8 相关方面的专家,但这对我来说似乎是一个错误。
  • 似乎不只是换行符:"こんにちは" =~ /a?\z/ => nil。
  • 在Ruby trunk 中,该问题现已被接受为错误。希望它会得到修复。

标签: ruby regex encoding multibyte ruby-2.0


【解决方案1】:

您报告的问题肯定是RUBY_VERSION #=> "2.0.0" 的Regexp 的错误,但在之前的1.9 中已经存在,当编码允许__ENCODING__ #=> #<Encoding:UTF-8> 等多字节字符时

不依赖于 Linux,也可以在 OSX 和 Windows 中重现相同的行为。

在bug 8210 将得到修复的同时,我们可以通过隔离和了解出现问题的情况来提供帮助。 当适用于特定情况时,这对于任何解决方法也很有用。

我了解问题发生在以下情况:

  • 在字符串结尾\z之前搜索内容。
  • 字符串的最后一个字符是多字节。
  • 之前的搜索使用零或一模式?
  • 但搜索到的零或一个字符的数量少于最后一个字符的字节数。

这个bug可能是由于字节数和正则表达式引擎实际检查的字符数之间的误解造成的。

几个例子可能会有所帮助:

测试 1:最后一个字符:“は”是 3 个字节:

s = "んにちは"

在字符串结束前测试 ん [3 个字节] 的零个或一个:

s =~ /ん?\z/u   #=> 4"       # OK it works 3 == 3

当我们尝试使用 ç [2 字节]

s =~ /ç?\z/u   #=> nil       # KO: BUG when 3 > 2
s =~ /x?ç?\z/u #=> 4         # OK it works 3 == ( 1+2 )

当测试 \n [1 个字节] 的零或之一时

s =~ /\n?\z/u #=> nil"      # KO: BUG when 3 > 1
s =~ /\n?\n?\z/u #=> nil"   # KO: BUG when 3 > 2
s =~ /\n?\n?\n?\z/u #=> 4"  # OK it works 3 == ( 1+1+1)

通过 TEST1 的结果,我们可以断言:如果字符串的最后一个多字节字符是 3 个字节,那么“之前的零或一”测试仅在我们测试至少 3 个字节(而不是 3 个字符)之前。

测试 2:最后一个字符 "ç" 是 2 个字节

s = "in French there is the ç" 

检查 ん [3 个字节]" 中的零个或一个"

s =~ /ん?\z/u #=> 24        # OK 2 <= 3

检查 é [2 个字节] 中的零个或一个

s =~ /é?\z/u #=> 24         # OK 2 == 2
s =~ /x?é?\z/u #=> 24       # OK 2 < (2+1)

测试 \n [1 个字节] 中的零个或一个

s =~ /\n?\z/u    #=> nil    # KO 2 > 1  ( the BUG occurs )
s =~ /\n?\n?\z/u #=> 24     # OK 2 == (1+1)
s =~ /\n?\n?\n?\z/u #=> 24  # OK 2 < (1+1+1)

通过 TEST2 的结果,我们可以断言:如果字符串的最后一个多字节字符是 2 个字节,那么“之前的零或一”测试仅在我们检查至少 2 个字节(而不是 2字符)之前。

当多字节字符不在字符串末尾时,我发现它可以正常工作。

public gist with my test code available here

【讨论】:

  • 我根本无法理解这个答案。此外,您在正则表达式中有z,而不是\z。什么是“KO”?
  • 对不起,我昨晚发布答案时有点累,所以我错过了 \z 之前的 \。谢谢你让我知道。现在我确定了答案。请see the code 玩弄它。在我看来这是有道理的。
【解决方案2】:

在Ruby trunk 中,该问题现已被接受为错误。希望它会得到修复。

更新:Ruby 主干中发布了两个补丁。

【讨论】:

  • 更新:将合并错误修复。
猜你喜欢
  • 2021-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-17
  • 2020-05-05
  • 1970-01-01
  • 2011-07-14
  • 1970-01-01
相关资源
最近更新 更多