我正在尝试将字符串从ISO-8859-1编码转换为UTF-8,但我似乎无法使其工作。这是我在irb中所做的一个例子。
irb(main):050:0> string = 'Norrlandsvägen'
=> "Norrlandsvägen"
irb(main):051:0> string.force_encoding('iso-8859-1')
=> "Norrlandsv\xC3\xA4gen"
irb(main):052:0> string = string.encode('utf-8')
=> "Norrlandsvägen"
我不确定为什么iso-8859-1中的Norrlandsvägen将在utf-8中转换为Norrlandsvägen。
我尝试过编码,编码!,编码(destinationEncoding,originalEncoding),iconv,force_encoding,以及我能想到的各种奇怪的解决方法,但似乎没什么用。有人可以帮助我/指出我正确的方向吗?
Ruby新手仍然像疯了一样拉扯头发,但感谢所有回复...... :)
这个问题的背景:我正在编写一个宝石,它将从一些网站下载一个xml文件(将具有iso-8859-1编码)并将其保存在存储中,我想将其转换为utf-8第一。但像Norrlandsvägen这样的词语让我感到困惑。真的任何帮助将不胜感激!
[更新]:我意识到在irb控制台中运行这样的测试可能会给我不同的行为,所以这就是我在实际代码中所拥有的:
def convert_encoding(string, originalEncoding)
puts "#{string.encoding}" # ASCII-8BIT
string.encode(originalEncoding)
puts "#{string.encoding}" # still ASCII-8BIT
string.encode!('utf-8')
end
但最后一行给出了以下错误:
Encoding::UndefinedConversionError - "\xC3" from ASCII-8BIT to UTF-8
感谢@Amadan在下面给出的答案,我注意到\xC3
实际上会显示在irb中,如果你运行:
irb(main):001:0> string = 'ä'
=> "ä"
irb(main):002:0> string.force_encoding('iso-8859-1')
=> "\xC3\xA4"
我还尝试为string.encode(originalEncoding)
的结果分配一个新变量,但却出现了更奇怪的错误:
newString = string.encode(originalEncoding)
puts "#{newString.encoding}" # can't even get to this line...
newString.encode!('utf-8')
,错误为Encoding::UndefinedConversionError - "\xC3" to UTF-8 in conversion from ASCII-8BIT to UTF-8 to ISO-8859-1
我仍然在所有这些编码混乱中迷失方向,但我非常感谢所有回复并帮助每个人都给了我!万分感谢! :)
答案 0 :(得分:14)
您以UTF-8分配字符串。它包含ä
。 UTF-8用两个字节表示ä
。
string = 'ä'
string.encoding
# => #<Encoding:UTF-8>
string.length
# 1
string.bytes
# [195, 164]
然后强制将字节解释为它们是ISO-8859-1,而不实际更改基础表示。这不再包含ä
。它包含两个字符Ã
和¤
。
string.force_encoding('iso-8859-1')
# => "\xC3\xA4"
string.length
# 2
string.bytes
# [195, 164]
然后将其翻译为UTF-8
。由于这不是重新解释而是翻译,你保留两个字符,但现在以UTF-8编码:
string = string.encode('utf-8')
# => "ä"
string.length
# 2
string.bytes
# [195, 131, 194, 164]
您缺少的是,您最初不拥有ISO-8859-1字符串,就像您从Web服务那样 - 您有胡言乱语。幸运的是,这一切都在您的控制台测试中;如果您使用正确的输入编码阅读网站的响应,它应该都可以正常工作。
对于您的控制台测试,让我们演示如果您使用正确的ISO-8859-1字符串开始,它一切正常:
string = 'Norrlandsvägen'.encode('iso-8859-1')
# => "Norrlandsv\xE4gen"
string = string.encode('utf-8')
# => "Norrlandsvägen"
编辑针对您的具体问题,这应该有效:
require 'net/https'
uri = URI.parse("https://rusta.easycruit.com/intranet/careerbuilder_se/export/xml/full")
options = {
:use_ssl => uri.scheme == 'https',
:verify_mode => OpenSSL::SSL::VERIFY_NONE
}
response = Net::HTTP.start(uri.host, uri.port, options) do |https|
https.request(Net::HTTP::Get.new(uri.path))
end
body = response.body.force_encoding('ISO-8859-1').encode('UTF-8')
答案 1 :(得分:1)
force_encoding和encode之间存在差异。前者设置字符串的编码,而后者实际上将字符串的内容转码为新编码。因此,以下代码会导致您的问题:
string = "Norrlandsvägen"
string.force_encoding('iso-8859-1')
puts string.encode('utf-8') # Norrlandsvägen
以下代码实际上会正确编码您的内容:
string = "Norrlandsvägen".encode('iso-8859-1')
string.encode!('utf-8')
以下是irb
中运行的示例:
irb(main):023:0> string = "Norrlandsvägen".encode('iso-8859-1')
=> "Norrlandsv\xE4gen"
irb(main):024:0> string.encoding
=> #<Encoding:ISO-8859-1>
irb(main):025:0> string.encode!('utf-8')
=> "Norrlandsvägen"
irb(main):026:0> string.encoding
=> #<Encoding:UTF-8>
答案 2 :(得分:0)
上面的答案是正确的。这里具体说明这一点:
force_encoding和encoding之间有区别。前者 设置字符串的编码,而实际上 将字符串的内容转码为新的编码。
在我的情况下,我有一个带有iso-8859-1编码的文本文件。默认情况下,Ruby使用UTF-8编码,因此,如果您尝试在不指定编码的情况下读取文件,则会出现错误:
results = File.read(file)
results.encoding
=> #<Encoding:UTF-8>
results.split("\r\n")
ArgumentError: invalid byte sequence in UTF-8
您将收到无效的字节序列错误,因为不同编码中的字符由不同的字节长度表示。因此,您需要为File API指定编码。可以将其视为force_encoding:
results = File.read(file, encoding: "iso-8859-1")
所以一切都好吧?不,如果要开始使用UTF-8字符编码解析iso-8859-1字符串,则不会:
results = File.read(file, encoding: "iso-8859-1")
results.each do |line|
puts line.split('¬')
end
Encoding::CompatibilityError: incompatible character encodings: ISO-8859-1 and UTF-8
为什么会出现此错误?因为“¬”表示为UTF-8。您正在对ISO-8859-1字符串使用UTF-8字符序列。它们是不兼容的编码。因此,在您将文件读为ISO-8859-1之后,您可以要求Ruby将ISO-8859-1编码为UTF-8。现在您将使用UTF-8字符串,因此没有问题:
results = File.read(file, encoding: "iso-8859-1").encode('UTF-8')
results.encoding
results = results.split("\r\n")
results.each do |line|
puts line.split('¬')
end
最终,对于某些Ruby API,您不需要使用force_encoding('ISO-8859-1')
。相反,您只需为API指定期望的编码。但是,如果计划使用UTF-8字符串解析它,则必须将其转换回UTF-8。