Ruby转换字符串编码从ISO-8859-1到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,以及我能想到的各种奇怪的解决方法,但似乎没有任何效果。 有人可以帮助我/指出我正确的方向吗?

Ruby新手仍然像疯了一样拉头发,但感谢所有回复… 🙂

这个问题的背景:我正在编写一个gem,它将从一些网站下载一个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 

感谢\xC3在下面的回答,我注意到如果你运行的话, \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 # => # 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和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 => # irb(main):025:0> string.encode!('utf-8') => "Norrlandsvägen" irb(main):026:0> string.encoding => # 

上面的答案是现货。 具体这一点在这里:

force_encoding和encode之间存在差异。 前者设置字符串的编码,而后者实际上将字符串的内容转码为新编码。

在我的情况下,我有一个iso-8859-1编码的文本文件。 默认情况下,Ruby使用UTF-8编码,因此如果您尝试在不指定编码的情况下读取文件,则会出现错误:

 results = File.read(file) results.encoding => # 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。