Ruby 不兼容的字符编码
Ruby incompatible character encodings
我目前正在尝试编写一个脚本来遍历输入文件并检查网站上的数据。如果它找到新数据,它会打印到它通过的终端,如果没有,它会告诉我它失败了。反之亦然删除的数据。它工作正常,直到我收到的输入文件包含“™”字符。然后当 ruby 到达该行时,它会吐出一个错误:
PDAPWeb.rb:73:in `include?': incompatible character encodings: UTF-8 and IBM437
(Encoding::CompatibilityError)
违规行是一个简单的检查,以查看该文本是否存在于页面上。
if browser.text.include? (program_name)
其中 program_name 变量是来自输入文件的已解析信息。在本例中,program_name 包含前面提到的 'TM' 字符。
经过一些研究,我发现在我的脚本开头添加行 # encoding: utf-8 可能会有所帮助,但到目前为止还没有证明有用。
我将它添加到我的 program_name 变量中以查看它是否有帮助(并且它允许我的脚本 运行 没有错误),但现在它无法正确找到 TM 字符应该。
program_name = record[2].gsub("\n", '').force_encoding("utf-8").encode("IBM437", replace: nil)
这似乎将 TM 字符转换为:Γäó
我想也许我把 IBM437 和 utf-8 部分颠倒了,所以我尝试了相反的
program_name = record[2].gsub("\n", '').force_encoding("IBM437").encode("utf-8", replace: nil)
现在我在尝试 运行 脚本
时收到此错误
PDAPWeb.rb:48:in `encode': U+2122 from UTF-8 to IBM437 (Encoding::UndefinedConve
rsionError)
我正在使用 ruby 1.9.3p392 (2013-02-22),我不确定是否应该升级,因为这是我公司安装的标准版本。
是不是我的编码不正确,导致TM字符转换错误?
事情是这样的。您的输入文件包含一个 ™
字符,并且采用 UTF-8 编码。然而,当您阅读它时,由于您没有指定编码,Ruby 假定它是您系统的默认编码 IBM437(您必须使用 Windows)。
这与此基本相同:
>> input = "™"
=> "™"
>> input.encoding
=> #<Encoding:UTF-8>
>> input.force_encoding 'ibm437'
=> "\xE2\x84\xA2"
请注意 force_encoding
不会更改实际的字符串,只会更改与其关联的标签。这与您的情况的结果相同,只是您通过不同的路线(通过读取文件)到达这里。
该网页也有一个 ™
符号,也被编码为 UTF-8,但在这种情况下 Ruby 的编码是正确的(Watir 可能使用 headers来自页面):
>> web_page = '™'
=> "™"
>> web_page.encoding
=> #<Encoding:UTF-8>
现在,当您尝试比较这两个字符串时,会出现兼容性错误,因为它们的编码不同:
>> web_page.include? input
Encoding::CompatibilityError: incompatible character encodings: UTF-8 and IBM437
from (irb):11:in `include?'
from (irb):11
from /Users/matt/.rvm/rubies/ruby-2.2.1/bin/irb:11:in `<main>'
如果两个字符串中的任何一个仅包含 ASCII 字符(即代码点小于 128),那么此比较将有效。 UTF-8 和 IBM437 都是 ASCII 的超集,只有当它们都包含 ASCII 范围之外的字符时才不兼容。这就是为什么您仅在输入文件具有 ™
.
时才开始看到此行为的原因
解决方法是告知 Ruby 输入文件的实际编码是什么。您可以使用已加载的字符串执行此操作:
>> input.force_encoding 'utf-8'
=> "™"
您也可以在 reading the file 时执行此操作,例如(有几种读取文件的方法,它们都应该允许您明确指定编码):
input = File.read("input_file.txt", :encoding => "utf-8")
# now input will be in the correct encoding
注意这两个字符串都没有改变,它仍然包含相同的字节,但是 Ruby 现在知道它的正确编码。
现在比较应该可以正常工作了:
>> web_page.include? input
=> true
不需要encode
字符串。这是如果你这样做会发生什么。首先,如果您将编码更正为 UTF-8,然后编码为 IBM437:
>> input.force_encoding("utf-8").encode("IBM437", replace: nil)
Encoding::UndefinedConversionError: U+2122 from UTF-8 to IBM437
from (irb):16:in `encode'
from (irb):16
from /Users/matt/.rvm/rubies/ruby-2.2.1/bin/irb:11:in `<main>'
IBM437 不包含 ™
字符,因此您不能将包含它的字符串编码为这种编码而不会丢失数据。默认情况下 Ruby 会在发生这种情况时引发异常。您可以使用 :undef
选项强制编码,但符号会丢失:
>> input.force_encoding("utf-8").encode("IBM437", :undef => :replace)
=> "?"
如果你走另一条路,首先使用 force_encoding
到 IBM437,然后编码为 UTF-8,你会得到字符串 Γäó
:
>> input.force_encoding("IBM437").encode("utf-8", replace: nil)
=> "Γäó"
就 Ruby 而言,该字符串已经采用 IBM437 编码,因此 force_encoding
不执行任何操作。 ™
的 UTF-8 表示是三个字节 0xe2 0x84 0xa2
,当解释为 IBM437 时,这些字节对应于此处看到的三个字符,然后将其转换为它们的 UTF-8 表示。
(这两个结果与您在问题中描述的相反,因此我在上面发表评论。我假设这只是一个 copy-and-paste 错误。)
我目前正在尝试编写一个脚本来遍历输入文件并检查网站上的数据。如果它找到新数据,它会打印到它通过的终端,如果没有,它会告诉我它失败了。反之亦然删除的数据。它工作正常,直到我收到的输入文件包含“™”字符。然后当 ruby 到达该行时,它会吐出一个错误:
PDAPWeb.rb:73:in `include?': incompatible character encodings: UTF-8 and IBM437 (Encoding::CompatibilityError)
违规行是一个简单的检查,以查看该文本是否存在于页面上。
if browser.text.include? (program_name)
其中 program_name 变量是来自输入文件的已解析信息。在本例中,program_name 包含前面提到的 'TM' 字符。
经过一些研究,我发现在我的脚本开头添加行 # encoding: utf-8 可能会有所帮助,但到目前为止还没有证明有用。
我将它添加到我的 program_name 变量中以查看它是否有帮助(并且它允许我的脚本 运行 没有错误),但现在它无法正确找到 TM 字符应该。
program_name = record[2].gsub("\n", '').force_encoding("utf-8").encode("IBM437", replace: nil)
这似乎将 TM 字符转换为:Γäó
我想也许我把 IBM437 和 utf-8 部分颠倒了,所以我尝试了相反的
program_name = record[2].gsub("\n", '').force_encoding("IBM437").encode("utf-8", replace: nil)
现在我在尝试 运行 脚本
时收到此错误PDAPWeb.rb:48:in `encode': U+2122 from UTF-8 to IBM437 (Encoding::UndefinedConve rsionError)
我正在使用 ruby 1.9.3p392 (2013-02-22),我不确定是否应该升级,因为这是我公司安装的标准版本。
是不是我的编码不正确,导致TM字符转换错误?
事情是这样的。您的输入文件包含一个 ™
字符,并且采用 UTF-8 编码。然而,当您阅读它时,由于您没有指定编码,Ruby 假定它是您系统的默认编码 IBM437(您必须使用 Windows)。
这与此基本相同:
>> input = "™"
=> "™"
>> input.encoding
=> #<Encoding:UTF-8>
>> input.force_encoding 'ibm437'
=> "\xE2\x84\xA2"
请注意 force_encoding
不会更改实际的字符串,只会更改与其关联的标签。这与您的情况的结果相同,只是您通过不同的路线(通过读取文件)到达这里。
该网页也有一个 ™
符号,也被编码为 UTF-8,但在这种情况下 Ruby 的编码是正确的(Watir 可能使用 headers来自页面):
>> web_page = '™'
=> "™"
>> web_page.encoding
=> #<Encoding:UTF-8>
现在,当您尝试比较这两个字符串时,会出现兼容性错误,因为它们的编码不同:
>> web_page.include? input
Encoding::CompatibilityError: incompatible character encodings: UTF-8 and IBM437
from (irb):11:in `include?'
from (irb):11
from /Users/matt/.rvm/rubies/ruby-2.2.1/bin/irb:11:in `<main>'
如果两个字符串中的任何一个仅包含 ASCII 字符(即代码点小于 128),那么此比较将有效。 UTF-8 和 IBM437 都是 ASCII 的超集,只有当它们都包含 ASCII 范围之外的字符时才不兼容。这就是为什么您仅在输入文件具有 ™
.
解决方法是告知 Ruby 输入文件的实际编码是什么。您可以使用已加载的字符串执行此操作:
>> input.force_encoding 'utf-8'
=> "™"
您也可以在 reading the file 时执行此操作,例如(有几种读取文件的方法,它们都应该允许您明确指定编码):
input = File.read("input_file.txt", :encoding => "utf-8")
# now input will be in the correct encoding
注意这两个字符串都没有改变,它仍然包含相同的字节,但是 Ruby 现在知道它的正确编码。
现在比较应该可以正常工作了:
>> web_page.include? input
=> true
不需要encode
字符串。这是如果你这样做会发生什么。首先,如果您将编码更正为 UTF-8,然后编码为 IBM437:
>> input.force_encoding("utf-8").encode("IBM437", replace: nil)
Encoding::UndefinedConversionError: U+2122 from UTF-8 to IBM437
from (irb):16:in `encode'
from (irb):16
from /Users/matt/.rvm/rubies/ruby-2.2.1/bin/irb:11:in `<main>'
IBM437 不包含 ™
字符,因此您不能将包含它的字符串编码为这种编码而不会丢失数据。默认情况下 Ruby 会在发生这种情况时引发异常。您可以使用 :undef
选项强制编码,但符号会丢失:
>> input.force_encoding("utf-8").encode("IBM437", :undef => :replace)
=> "?"
如果你走另一条路,首先使用 force_encoding
到 IBM437,然后编码为 UTF-8,你会得到字符串 Γäó
:
>> input.force_encoding("IBM437").encode("utf-8", replace: nil)
=> "Γäó"
就 Ruby 而言,该字符串已经采用 IBM437 编码,因此 force_encoding
不执行任何操作。 ™
的 UTF-8 表示是三个字节 0xe2 0x84 0xa2
,当解释为 IBM437 时,这些字节对应于此处看到的三个字符,然后将其转换为它们的 UTF-8 表示。
(这两个结果与您在问题中描述的相反,因此我在上面发表评论。我假设这只是一个 copy-and-paste 错误。)