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

LPr*_*Prc 5 ruby encoding utf-8

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

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

:key1:
  :key2: "ä"
Run Code Online (Sandbox Code Playgroud)

utf8.yml编码UTF-8,ansi.yml编码ANSI.我加载文件如下:

# 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')
Run Code Online (Sandbox Code Playgroud)

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"
Run Code Online (Sandbox Code Playgroud)

因为字节不一样:

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]
Run Code Online (Sandbox Code Playgroud)

如果我错过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]
Run Code Online (Sandbox Code Playgroud)
  1. 居然会发生什么事,我不设置default_internalutf-8
  2. 两个例子中的字符串都有哪些编码?
  3. 即使我不知道它的编码,我怎么能加载文件?

the*_*Man 5

YAML规范在“在“ 5.1.字符集

\n\n
\n

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

\n
\n\n

这意味着只要输出的字符在定义的范围内,Windows-1252 或 ISO-8859-1 编码都是可接受的。Windows 用户倾向于使用“C1 控制块 #x80-#x9F”范围来表示变音符号和重音字符,因此,如果 YAML 文件中存在这些字符,则该文件将不符合规范,并且 YAML 生成器也不符合规范正确地完成其工作。这解释了为什么"\xc3\xa4"不可接受。

\n\n
\n

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

\n
\n\n

目前,Ruby 默认使用 UTF-8,但 YAML 并不限于此。该规范在“ 5.2.字符编码”中继续说道

\n\n
\n

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

\n\n

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

\n
\n\n

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

\n


jro*_*ind 4

我相信官方 YAML 仅支持 UTF-8(也许还有 UTF-16)。历史上,YAML 库中存在各种编码混乱。我认为尝试使用 Unicode 编码以外的其他方式使用 YAML 会遇到麻烦。

\n\n
\n
    \n
  1. 当我不将 default_internal 设置为 utf-8 时,实际上会发生什么?
  2. \n
\n
\n\n

Encoding.default_internal控制您的输入在读入时将转换为的Encoding.default_internal编码,至少通过一些尊重的操作,而不是所有的操作。Rails 似乎将其设置为 UTF-8。因此,如果您不将Encoding.default_internalUTF-8 设置为 UTF-8,那么它可能已经是 UTF-8 了。

\n\n

如果Encoding.default_internalnil,那么那些尊重它的操作,并在读取它时尝试将任何输入转换为Encoding.default_internal不会这样做,它们会将任何输入保留在它被认为源自的编码中,而不是尝试转换它。

\n\n

如果您将其设置为其他内容,例如“WINDOWS-1252”,当使用 读取它时,Ruby 会自动将您的内容转换为 WINDOWS-1252 ,当您传递现在编码并标记为的字符串时,File.open这可能会造成混淆YAML::loadWINDOWS-1252 就可以了。一般来说,没有充分的理由这样做,所以不要管Encoding.default_internal

\n\n

注意:Ruby 文档说:

\n\n
\n

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

\n
\n\n

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

\n\n
\n
    \n
  1. 这两个示例中的字符串都采用哪些编码?
  2. \n
\n
\n\n

我真的不知道。人们必须查看字节并尝试弄清楚它们是否是各种看似合理的编码的合法字节,并且除了合法之外,它们是否意味着可能的意图。

\n\n

例如采取:"\xc3\x89G\xc3\x89\xc3\xac\xc3\x89R\xc3\x85[\xc3\x89f\xc3\x89B\xc3\x89\xc3\xac\xc3\x89O\xc3\x87\xc3\x95\xc3\xac\xc3\x94\xc3\x87\xc2\xb5\xc3\x87\xe2\x89\xa0\xc3\x87\xc2\xbb\xc3\x87\xc2\xa2". 这是一个完全合法的UTF-8 字符串,但作为人类,我们知道它可能不是有意的,而且可能是垃圾,很可能是编码误解的结果。但计算机无法知道这一点,它是完全合法的 UTF-8,而且,嘿,也许有人确实想写"\xc3\x89G\xc3\x89\xc3\xac\xc3\x89R\xc3\x85[\xc3\x89f\xc3\x89B\xc3\x89\xc3\xac\xc3\x89O\xc3\x87\xc3\x95\xc3\xac\xc3\x94\xc3\x87\xc2\xb5\xc3\x87\xe2\x89\xa0\xc3\x87\xc2\xbb\xc3\x87\xc2\xa2",毕竟,我在写这篇文章时就是这么做的!

\n\n

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

\n\n

此时您实际上只是在猜测。意思是...

\n\n
\n
    \n
  1. 即使我不知道文件的编码,如何加载文件?
  2. \n
\n
\n\n

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

\n\n

如果您丢失了一些遗留数据,则必须尝试找出答案。手动或使用一些代码尝试根据启发式猜测可能的编码。这是Charlock Holmes试图使用 ICU 库启发法进行猜测的一个 Ruby gem(这个特殊的 gem 仅适用于 MRI)。

\n\n

Ruby 响应的string.encoding只是字符串标记的编码该字符串可能用错误的编码进行标记,字符串中的字节实际上并不意味着它所标记的编码的意图...在这种情况下,您将得到垃圾。

\n\n

仅当字符串的编码标记正确时,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正确的编码,以将这些字节解释为文本,以表达其本来的含义。

\n\n

最近在 reddit /r/ruby 上发布了几篇文章,试图解释如何解决和解决编码问题,您可能会发现这些问题很有帮助:

\n\n\n\n

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

\n\n

特别是对于 YAML 文件,如果我是您,我只会确保它们全部采用 UTF-8 格式。生活会变得更加轻松,您不必担心。如果您有一些已损坏的遗留文件,那么修复它们将是一件痛苦的事情,但这就是您必须要做的,除非您可以从头开始重写它们。尝试将它们修复为有效且正确的 UTF-8,并从现在开始将所有 YAML 保留为 UTF-8。

\n

  • 我想进一步补充一下——设置 Encoding.default_internal 告诉 ruby​​ 在读入数据时可能对其进行_transcode_。这意味着您将要查看的字节将不再是磁盘上实际存在的字节。如果 external_encoding 错误,也意味着你会得到垃圾。将 Encoding.default_internal 保留为 nil,尤其是在尝试弄清楚发生了什么情况时,这只会让事情变得混乱。 (2认同)