我很难弄清楚为什么rindex()在我的Rails3.0.7应用程序(ruby 1.8.7)中对商标字符引发异常:
irb(main):007:0> "™ foo™".mb_chars.rindex(/W/)
ActiveSupport::Multibyte::EncodingError: malformed UTF-8 character
from /opt/local/lib/ruby/gems/1.8/gems/activesupport-3.0.7/lib/active_support/multibyte/unicode.rb:72:in `u_unpack'
from /opt/local/lib/ruby/gems/1.8/gems/activesupport-3.0.7/lib/active_support/multibyte/chars.rb:167:in `rindex'
from (irb):7
irb(main):008:0> "™ foo™".mb_chars.index(/W/)
=> 1
但这很好用。
irb(main):009:0> "® foo®".mb_chars.rindex(/W/)
=> 1
irb(main):010:0> "® foo®".mb_chars.index(/W/)
=> 1
我怀疑您的源代码编码可能是原因。检查以查看此字符串在运行时包含的确切内容。这可能会对你有所帮助http://ruby-unicode.rubyforge.org/doc/
这似乎是ActiveSupport中的一个错误。
以下是如何可靠地复制它,而不需要粘贴字符(这通常是不可靠的)。
相关代码点为:
TM symbol = 0x2122
registered symbol = 0xAE
代码:
$KCODE = 'u'
tm_char = [0x2122].pack('U')
r_char = [0xAE].pack('U')
tm_char.mb_chars.rindex(/W/) # error: malformed utf-8
r_char.mb_chars.rindex(/W/) # ok, but I expected 0 instead of nil
tm_char.mb_chars.rindex(tm_char) # ok. but we're not using a regexp
我怀疑这与TM是一个3字节的UTF-8字符有关,而"(R)"是一个2字节的
tm_char.bytes.to_a.inspect
r_char.bytes.to_a.inspect
同样的情况也发生在mdash(0x2014)上。
Ruby 1.9.2没有这个问题:
[0x2122].pack('U').mb_chars.rindex(/W/) # => 0
[0x2014].pack('U').mb_chars.rindex(/W/) # => 0
这里有一个丑陋的解决方法,可以找到最后一个非单词字符及其索引。它甚至可以正常工作,而不是忽略多字节符号。
mb_string = "#{tm_char} foo#{tm_char}".mb_chars
match, rev_idx = mb_string.chars.to_a.reverse.each_with_index.detect{|e,idx| e =~ /[^a-zA-Z0-9]/ }
idx = mb_string.size - rev_idx - 1 # => 5