将非 ASCII 字符从 ASCII-8BIT 转换为 UTF-8

Posted

技术标签:

【中文标题】将非 ASCII 字符从 ASCII-8BIT 转换为 UTF-8【英文标题】:Convert non-ASCII chars from ASCII-8BIT to UTF-8 【发布时间】:2011-06-19 13:36:51 【问题描述】:

我正在从远程站点提取文本并尝试将其加载到默认使用 utf-8 的 Ruby 1.9/Rails 3 应用程序中。

以下是一些冒犯性文字的示例:

Cancer Res; 71(3); 1-11. ©2011 AACR.\n

扩展后的版权代码如下所示:

Cancer Res; 71(3); 1-11. \xC2\xA92011 AACR.\n

Ruby 告诉我字符串被编码为 ASCII-8BIT 并输入到我的 Rails 应用程序中得到这个:

incompatible character encodings: ASCII-8BIT and UTF-8

我可以使用这个正则表达式去除版权代码

str.gsub(/[\x00-\x7F]/n,'?')

制作这个

Cancer Res; 71(3); 1-11. ??2011 AACR.\n

但是如何将版权符号(以及各种其他符号,如希腊字母)转换 为 UTF-8 中的相同符号?当然有可能……

我看到对使用 force_encoding 的引用,但这不起作用:

str.force_encoding('utf-8').encode

我知道还有很多其他人有类似的问题,但我还没有看到有效的解决方案。

【问题讨论】:

如何从远程站点提取文本?抓取页面?请显示一些示例代码,包括您使用的 HTTP 客户端,以及您是否使用 Nokogiri、Hpricot 或 ReXML 解析页面。此问题可能是您检索页面的方式和/或解析页面的方式造成的。一旦我们知道您以数据安全的方式提取内容,我们就可以帮助您在代码集之间转换数据。 真正的简单代码 - open-uri 和 nokogiri - 例如doc = Nokogiri::XML(open(url)) 然后 doc.css(...).text 提取相关的文本块 请显示示例代码。您要检索的文件是 html 还是 XML? Nokogiri 在解析时确实关心差异。另外,请提供一些 URL,因为 Internet 上的每个站点都是不同的。 “我看到使用 force_encoding 的引用,但这不起作用” “不起作用”是什么意思?它会引发错误吗? Ruby 是否存在段错误?你的电脑着火了吗?它会用 Yankee Doodle Dandy 的歌词替换字符串的内容吗?请详细点! :) 【参考方案1】:

这对我有用:

#encoding: ASCII-8BIT
str = "\xC2\xA92011 AACR"
p str, str.encoding
#=> "\xC2\xA92011 AACR"
#=> #<Encoding:ASCII-8BIT>

str.force_encoding('UTF-8')
p str, str.encoding
#=> "©2011 AACR"
#=> #<Encoding:UTF-8>

【讨论】:

这可能会导致invalid byte sequence in UTF-8 错误。我建议你改用encode('UTF-8') 这也适用于我,但其他字符串不适用。例如:str = "Diario El d\xEDa Bolivia" 不会转换为 "Diario El día Bolivia"。 这很奇怪,"\xC2\xA92011 AACR" sn-p 为我返回 UTF-8 "©2011 AACR" #&lt;Encoding:UTF-8&gt; @MikeR 你的文件顶部有encoding magic comment吗? @Phrogz 不,我刚刚打开了一个 irb 会话(我在 ubuntu 上使用 ruby​​-2.2.1)并复制粘贴了这两行。【参考方案2】:

有两种可能:

    输入数据已经是 UTF-8,但 Ruby 只是不知道。这似乎是你的情况,因为 "\xC2\xA9" 是版权符号的有效 UTF-8。在这种情况下,您只需要使用 force_encoding 告诉 Ruby 数据已经是 UTF-8。

    例如 "\xC2\xA9".force_encoding('ASCII-8BIT') 将重新创建输入数据的相关位。而 "\xC2\xA9".force_encoding('ASCII-8BIT').force_encoding('UTF-8') 将证明你可以告诉 Ruby 它真的是 UTF-8 并得到想要的结果。

    输入数据采用其他编码,您需要 Ruby 将其转码为 UTF-8。在这种情况下,您必须告诉 Ruby 当前的编码是什么(ASCII-8BIT 是 ruby​​-speak 的二进制,它不是真正的编码),然后告诉 Ruby 对其进行转码。

    例如,假设您的输入数据是 ISO-8859-1。在该编码中,版权符号只是“\xA9”。这将生成这样一些数据: "\xA9".force_encoding('ISO-8859-1') 这将证明您可以让 Ruby 将其转码为 UTF-8: "\xA9".force_encoding('ISO -8859-1').encode('UTF-8')

【讨论】:

我在使用 rails + sql server 时遇到了这种错误。解决了在 database.yml 中设置“encoding: ISO-8859-1”并使用“latting string”.encode("UTF-8") 完美。 #2 解决了我的问题,也通过 Ruby/DBI 从 Sql Server 中提取。 @Lucas Renan:感谢您对 rails/database.yml 的关注。稍后我可能会为站点添加导轨。【参考方案3】:

我曾经为一个使用 open-uri、iconv 和 Hpricot 抓取希腊 Windows 编码页面的脚本执行此操作:

doc = open(DATA_URL)
doc.rewind
data = Hpricot(Iconv.conv('utf-8', "WINDOWS-1253", doc.readlines.join("\n")))

我相信那是 Ruby 1.8.7,不知道 ruby​​ 1.9 的情况如何

【讨论】:

谢谢!以上都没有为我处理“\x96”,仍然会爆炸。 Iconv.conv('utf-8', "WINDOWS-1253", str) 完美运行。 您可能还需要将 Iconv 的 transliterate 值设置为 true。 ruby-doc.org/stdlib-1.9.2/libdoc/iconv/rdoc/…【参考方案4】:

我一直遇到字符编码问题,其他答案很有帮助,但并不适用于所有情况。这是我想出的解决方案,它在可能的情况下强制编码,在不可能的情况下使用'?'进行转码。这是解决方案:

  def encode str
    encoded = str.force_encoding('UTF-8')
    unless encoded.valid_encoding?
      encoded = str.encode("utf-8", invalid: :replace, undef: :replace, replace: '?')
    end
    encoded
  end

force_encoding 大部分时间都有效,但我遇到了一些失败的字符串。像这样的字符串将替换无效字符:

 str = "don't panic: \xD3"
 str.valid_encoding?
 false
 str = str.encode("utf-8", invalid: :replace, undef: :replace, replace: '?')
 "don't panic: ?"
 str.valid_encoding?
 true

更新:我在使用上述代码时遇到了一些问题。我建议您使用已知问题文本设置单元测试,以确保此代码适合您,就像您需要的那样。一旦我提出第 2 版,我将更新此答案。

【讨论】:

以上是关于将非 ASCII 字符从 ASCII-8BIT 转换为 UTF-8的主要内容,如果未能解决你的问题,请参考以下文章

使用带有无效字符的 Net::FTP gettextfile (ASCII-8BIT vs UTF-8)

ASCII-8BIT 中的 Rails 编码

不兼容的字符编码:ASCII-8BIT 和 UTF-8

ruby `encode': "\xC3" 从 ASCII-8BIT 到 UTF-8 (Encoding::UndefinedConversionError)

Amazon Code Deploy 中从 ASCII-8BIT 到 UTF-8 的“\xCB”

mysql2 gem 0.3.15 提供编码设置为“utf8”的 ASCII-8BIT