导入YAML文件时如何使用内部/外部编码?

时间:2015-09-23 20:26:12

标签: ruby encoding utf-8

如何加载YAML文件而不管其编码?

我的YAML文件可以用UTF-8或ANSI编码(这就是Notepad ++所说的 - 我猜它是Windows-1252):

:key1:
  :key2: "ä"

utf8.ymlUTF-8编码,ansi.ymlANSI编码。我按如下方式加载文件:

# encoding: utf-8

Encoding.default_internal = "utf-8"

utf8_load      = YAML::load(File.open('utf8.yml'))
utf8_load_file = YAML::load_file('utf8.yml')
ansi_load      = YAML::load(File.open('ansi.yml'))
ansi_load_file = YAML::load_file('ansi.yml')

Ruby似乎无法正确识别编码:

utf8_load      [:key1][:key2].encoding  #=> "UTF-8"
utf8_load_file [:key1][:key2].encoding  #=> "UTF-8"
ansi_load      [:key1][:key2].encoding  #=> "UTF-8"
ansi_load_file [:key1][:key2].encoding  #=> "UTF-8"

因为字节不相同:

utf8_load      [:key1][:key2].bytes  #=> [195, 164]
utf8_load_file [:key1][:key2].bytes  #=> [195, 164]
ansi_load      [:key1][:key2].bytes  #=> [239, 191, 189]
ansi_load_file [:key1][:key2].bytes  #=> [239, 191, 189]

如果我错过Encoding.default_internal = "utf-8",字节也不同:

utf8_load      [:key1][:key2].bytes  #=> [195, 131, 194, 164]
utf8_load_file [:key1][:key2].bytes  #=> [195, 164]
ansi_load      [:key1][:key2].bytes  #=> [195, 164]
ansi_load_file [:key1][:key2].bytes  #=> [239, 191, 189]
  1. 当我未将default_internal设置为utf-8时会发生什么?
  2. 两个例子中的字符串都有哪些编码?
  3. 即使我不知道其编码,我如何加载文件?

2 个答案:

答案 0 :(得分:4)

YAML specification在" 5.1. Character Set":

中说
  

为确保可读性,YAML流仅使用Unicode字符集的可打印子集。允许的字符范围明确排除C0控制块#x0-#x1F(允许的TAB#x9,LF #xA和CR #xD除外),DEL#x7F,C1控制块#x80-#x9F(除外)对于允许的NEL#x85),代理块#xD800-#xDFFF,#xFFFE和#xFFFF。

这意味着只要输出的字符在定义的范围内,Windows-1252或ISO-8859-1编码就可以接受。 Windows用户倾向于使用" C1控制块#x80-#x9F"变量字符和重音字符的范围,因此如果这些字符存在于YAML文件中,则该文件将不符合规范,并且YAML生成器无法正确执行其工作。这解释了为什么"ä"不可接受。

  

在输出时,YAML处理器必须只生成可接受的字符。必须使用转义序列显示任何排除的字符。此外,还应转义任何已知为不可打印的允许字符。这不是强制性的,因为完整的实现需要大量的字符属性表。

目前,默认情况下,Ruby使用UTF-8,但YAML并不限于此。该规范继续在" 5.2. Character Encodings":

中说
  

在输入时,YAML处理器必须支持UTF-8和UTF-16字符编码。对于JSON兼容性,还必须支持UTF-32编码。

     

如果字符流以字节顺序标记开头,则字符编码将被视为字节顺序标记所指示的。否则,流必须以ASCII字符开头。这允许通过null(#x00)字符的模式推断编码。

因此,支持UTF-8,16和32,但Ruby将采用UTF-8。如果BOM存在,您在编辑器中查看文件时会看到它。我还没有尝试加载一个UTF-16或32文件来查看Ruby的YAML做什么,所以这就是一个实验。

答案 1 :(得分:3)

我认为YAML官方只支持UTF-8(也许是UTF-16)。历史上,YAML库中存在各种编码混淆。我认为你会遇到麻烦试图让YAML不是Unicode编码。

  
      
  1. 当我没有将default_internal设置为utf-8时会发生什么?
  2.   

Encoding.default_internal控制输入在读入时转换为的编码,至少是某些尊重Encoding.default_internal的操作,而不是所有操作。 Rails似乎将其设置为UTF-8。因此,如果您未将Encoding.default_internal设置为UTF-8,则无论如何都可能是UTF-8。

如果Encoding.default_internalnil,那么尊重它的那些操作,并尝试将任何输入转换为Encoding.default_internal时,不会这样做,他们会&#39} ; ll保留其认为源自的编码中的任何输入,而不是尝试转换它。

如果您将其设置为其他内容,例如说" WINDOWS-1252"当你使用File.open读取它时,Ruby会自动将你的东西转换为WINDOWS-1252,当你传递现在编码并标记为WINDOWS-1252的字符串时,这可能会混淆YAML::load它。通常没有充分的理由这样做,所以请单独留下Encoding.default_internal

注意:Ruby docs说:

  

"你不应该在Ruby代码中设置:: default_internal,因为在更改值之前创建的字符串可能与更改后创建的字符串具有不同的编码。相反,你应该使用ruby -E来使用正确的default_internal来调用Ruby。"

另请参阅:http://ruby-doc.org/core-1.9.3/Encoding.html#method-c-default_internal

  
      
  1. 两个例子中的字符串都有哪些编码?
  2.   

我真的不知道。人们必须要查看字节并尝试确定它们是否是各种合理编码的合法字节,并且除了合法之外,如果它们意味着可能有意图的话。

例如:"ÉGÉìÉRÅ[ÉfÉBÉìÉOÇÕìÔǵÇ≠ǻǢ"。这是一个完美的合法 UTF-8字符串,但作为人类,我们知道可能无意,可能是垃圾,很可能是编码误解的结果。但是计算机无法知道这一点,它是完全合法的UTF-8,而且,嘿,也许有人确实有意写"ÉGÉìÉRÅ[ÉfÉBÉìÉOÇÕìÔǵÇ≠ǻǢ",毕竟,我刚写完这篇文章!

因此,您可以尝试根据各种编码解释字节,看看它们是否有意义。

你真的只是猜测这一点。这意味着......

  
      
  1. 即使我不知道它的编码,我怎样才能加载文件?
  2.   

一般来说,你不能。您需要了解并跟踪编码。在不知道编码的情况下,没有真正的方法可以知道字节的含义。

如果您有一些丢失了这些遗留数据的遗留数据,那么您必须尝试解决这个问题。手动或使用一些代码尝试根据启发式猜测可能的编码。这是一个试图猜测的Ruby gem Charlock Holmes,使用ICU库启发式(这个特殊的gem只适用于MRI)。

Ruby在响应string.encoding时所说的只是字符串标记的编码。该字符串可以使用错误的编码进行标记,字符串中的字节实际上意味着它所标记的编码中的内容......在这种情况下,您可以使用#&# 39;我会得到垃圾。

Ruby将使用您的字符串执行正确的操作,而不是仅在字符串的编码标记正确时才创建垃圾。对于大多数输入操作,字符串的编码标记由Encoding.default_external确定(Encoding.default_external通常以UTF-8开头,或ASCII-8BIT实际上意味着空编码,二进制数据,没有用编码标记),或者通过将参数传递给File.open:File.open("something", "r:UTF-8"或者,意思相同,File.open("something", "r", :encoding => "UTF-8")。实际字节由文件中的任何内容确定。您可以告诉Ruby 正确的编码将这些字节解释为文本意味着它们的意思。

最近有几篇关于reddit / r / ruby​​的帖子试图解释如何排除故障并解决您可能会觉得有用的编码问题:

此外,这是我最喜欢的关于编码的文章:http://kunststube.net/encoding/

特别是对于YAML文件,如果我是你,我只是确保它们都是UTF-8。生活会更容易,你不必担心它。如果你有一些已经被破坏的遗留物,那么修复它们会很痛苦,但这是你必须做的,除非你可以从头开始重写它们。尝试将它们修复为有效且正确的UTF-8,并从此处开始将所有YAML保存为UTF-8。