【问题标题】:malformed UTF-8 character when calling rindex() on string containing trademark symbol在包含商标符号的字符串上调用 rindex() 时出现格式错误的 UTF-8 字符
【发布时间】:2011-09-06 14:32:01
【问题描述】:

我很难弄清楚为什么 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

【问题讨论】:

    标签: ruby ruby-on-rails-3 unicode utf-8


    【解决方案1】:

    我怀疑您的源代码编码可能是原因。检查该字符串在运行时包含的确切内容。这可能会帮助你http://ruby-unicode.rubyforge.org/doc/

    【讨论】:

    • irb(main):009:0> "™ foo™".each_char {|c| puts "#{c.ord}:#{c}"}226:™32: 102:f111:o111:o226:™226:™irb(main):010:0> "™ foo™".is_utf8?=> trues
    • 抱歉,格式不太好。控制台 sn -p 显示字符串是 UTF8。如果事先在字符串上调用 mb_chars(),也会出现同样的结果。当 rindex() 是 UTF8 字符串中的最后一个字符并且您使用上面使用的正则表达式调用 rindex() 时,rindex() 似乎与 \226 有问题。
    【解决方案2】:

    这似乎是 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
    

    【讨论】:

      猜你喜欢
      • 2014-05-20
      • 1970-01-01
      • 2016-11-13
      • 1970-01-01
      • 2018-03-09
      • 2019-01-31
      • 2012-12-23
      • 1970-01-01
      • 2015-10-05
      相关资源
      最近更新 更多