【问题标题】:Determine character encoding in Ruby 1.9.3确定 Ruby 1.9.3 中的字符编码
【发布时间】:2012-04-05 16:23:41
【问题描述】:

我的 Rails 3.2.2 / Ruby 1.9.3 应用程序获取搜索请求,例如:

http://booko.com.au/books/search?q=Fran%E7ois+Vergniolle+de+Chantal

Ruby / Rails 接受此查询并对其进行解码 - 但假定它是 UTF-8。在某些时候我得到一个:

invalid byte sequence in UTF-8
app/models/product.rb:694:in `upcase' 

我认为它正在做这样的事情:

q="Fran%E7ois+Vergniolle+de+Chantal"
=> "Fran%E7ois+Vergniolle+de+Chantal"

CGI.unescape( q )
=> "Fran\xE7ois Vergniolle de Chantal"

CGI.unescape( q ).encoding.name
=> "UTF-8"

CGI.unescape( q ).valid_encoding?
=> false

处理这个问题的正确方法是什么?我想将其转码为正确的编码 - 但如何确定当前编码?我目前正在做的只是假设它是 LATIN1:

q.encode!("ISO-8859-1", "UTF-8", :invalid => :replace, :undef => :replace, :replace => "")

或者做一些我在某处博客上找到的事情:

q = q.unpack('C*').pack('U*')

处理这个问题的正确方法是什么?

编辑 服务器正确地向客户端发送“Content-Type: text/html; charset=utf-8”标头。该页面还包含适当的元标记:'meta http-equiv="content-type" content="text/html;charset=UTF-8"'

不确定是否有其他方法可以告诉客户端使用哪种编码?

【问题讨论】:

  • 如果在app/models/product.rb 的顶部写上# coding: UTF-8 会怎样。我认为它应该解决这个错误。您对这个解决方案满意吗?
  • @ck3g,不,这里不是文件编码。
  • 您必须使用某种字典才能确定正确的编码,因为相同的字节 0xE7 可能(并且确实是)在 Latin1 以外的编码中是有效字符。跨度>
  • @ck3g 数据来自网络请求,因此无济于事。应用程序已经认为它是 UTF-8,而实际上不是。
  • @MladenJablanović 是的 - 这将是一个解决方案。有这样的字典吗?由于 0xE7 存在于多种编码中,因此您希望按我猜最常见的方式进行排序 - 除非有多个字符来缩小选择范围。

标签: ruby ruby-on-rails-3 character-encoding


【解决方案1】:

字符 ç 在 URL 中编码为 %E7。这就是 ISO-8859-1 编码ç的方式。 ISO-8859-1 字符集表示具有单个字节的字符。代表ç的字节可以用十六进制表示为E7。

在 Unicode 中,ç 的代码点为 U+00E7。与 ISO-8859-1 不同,其中代码点 (E7) 与其编码(十六进制的 E7)相同,Unicode 具有多种编码方案,例如 UTF-8、UTF-16 和 UTF-32。 UTF-8 将 U+00E7 (ç) 编码为两个字节 - C3 A7。

请参阅here 了解其他编码ç的方法。

至于为什么 ISO-8859-1 中的 U+00E7 和 E7 都使用“E7”,Unicode 中的前 256 个码位与ISO-8859-1 相同。

如果此 URL 是 UTF-8,ç 将被编码为 %C3%A7。我对RFC2616 的(非常有限的)理解是,URL 的默认编码是(当前)ISO-8859-1。因此,这很可能是 ISO-8859-1 编码的 URL。这意味着,最好的方法可能是检查编码是否有效,如果不是,则假设它是 ISO-8859-1 并将其转码为 UTF-8:

unless query.valid_encoding?
    query.encode!("UTF-8", "ISO-8859-1", :invalid => :replace, :undef => :replace, :replace => "")
end

这是 IRB 中的过程(加上最后的转义)

a = CGI.unescape("%E7")
=> "\xE7"
a.encoding
=> #<Encoding:UTF-8>
a.valid_encoding?
=> false
b = a.encode("UTF-8", "ISO-8859-1")    # From ISO-8859-1 -> UTF-8
=> "ç"
b.encoding
=> #<Encoding:UTF-8>
CGI.escape(b)
=> "%C3%A7"

【讨论】:

    【解决方案2】:

    它似乎是一个 url 编码的字符串。 以下是编码字符列表供参考:http://www.degraeve.com/reference/urlencoding.php

    不幸的是,CGI 库在 utf-8 方面存在问题,如果 unescape 方法适用于某些字符(如空格),则它不适用于其他字符。

    require'cgi'
    a = "Fran%E7ois+Vergniolle+de+Chantal"
    a= a.gsub('+', ' ').gsub('%E7','ç')
    puts a
    => François Vergniolle de Chantal
    
    a = "Fran%E7ois+Vergniolle+de+Chantal"
    a = CGI::unescape(a) 
    puts a
    => Franis Vergniolle de Chantal
    

    也许您可以使用 gsub 和编码字符列表实现您自己的方法?

    【讨论】:

    • @MladenJablanović 如果字符串是 UTF-8,你不应该需要 force_encode 到 latin1 然后编码到 UTF-8 吗?因为 %E7 是两个字符集中带有 cedilla 的小 C?进一步阅读会表明 %C3%A7 可能是 UTF-8 下该字符的正确编码,而不是 %E7。
    • @Mladen Jabnović 您的代码确实有效,但我不喜欢多次强制编码。此外,CGI::unescape 以 string.tr('+', ' ').force_encoding(Encoding::ASCII_8BIT) 开头,我根本不喜欢这样:S
    猜你喜欢
    • 2015-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-22
    • 2013-01-13
    相关资源
    最近更新 更多