Ruby 无法将字符串编码从 ISO-8859-1 转换为 UTF-8
Ruby converting string encoding from ISO-8859-1 to UTF-8 not working
我正在尝试将字符串从 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,以及我能想到的各种奇怪的解决方法,但似乎没有任何效果。有人可以在正确的方向上帮助 me/point 我吗?
Ruby新手还在疯狂地拉着头发,但很感激这里的所有回复... :)
这个问题的背景:我正在写一个gem,它将从一些网站(将有iso-8859-1编码)下载一个xml文件并将其保存在一个存储和我想先把它转换成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
我仍然对所有这些编码混乱感到迷茫,但我真的很感谢大家给我的所有回复和帮助!万分感谢! :)
您分配一个 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')
force_encoding and 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>
上面的回答很正确。具体这点在这里:
There's a difference between force_encoding and encode. The former
sets the encoding for the string, whereas the latter actually
transcodes the contents of the string to the new 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
您收到无效字节序列错误,因为不同编码的字符由不同的字节长度表示。因此,您需要指定文件 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。
我正在尝试将字符串从 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,以及我能想到的各种奇怪的解决方法,但似乎没有任何效果。有人可以在正确的方向上帮助 me/point 我吗?
Ruby新手还在疯狂地拉着头发,但很感激这里的所有回复... :)
这个问题的背景:我正在写一个gem,它将从一些网站(将有iso-8859-1编码)下载一个xml文件并将其保存在一个存储和我想先把它转换成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
我仍然对所有这些编码混乱感到迷茫,但我真的很感谢大家给我的所有回复和帮助!万分感谢! :)
您分配一个 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')
force_encoding and 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>
上面的回答很正确。具体这点在这里:
There's a difference between force_encoding and encode. The former sets the encoding for the string, whereas the latter actually transcodes the contents of the string to the new 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
您收到无效字节序列错误,因为不同编码的字符由不同的字节长度表示。因此,您需要指定文件 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。