将非 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" #<Encoding:UTF-8>
@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)
ruby `encode': "\xC3" 从 ASCII-8BIT 到 UTF-8 (Encoding::UndefinedConversionError)