读取文件时没有指定编码。您非常小心地在除那里之外的任何地方指定它,但是您正在使用默认编码读取它。
File.open('foo.txt', "w:iso-8859-1") {|f| f << 'fòo'.force_encoding('iso-8859-1')}
File.open('foo.txt', "r:iso-8859-1") {|f| puts f.read().encoding }
# => ISO-8859-1
另请注意,您的意思可能是'fòo'.encode('iso-8859-1')
而不是'fòo'.force_encoding('iso-8859-1')
. 后者保持字节不变,而前者对字符串进行转码。
更新:我会详细说明一下,因为我没有尽可能清楚或彻底。
如果您不使用 指定编码File.read()
,则将使用 读取文件Encoding.default_external
。由于您没有自己设置,Ruby 使用的值取决于它运行的环境。在您的 Windows 环境中,它是 IBM437;在您的 Cygwin 环境中,它是 UTF-8。所以我上面的观点是,这当然就是编码。它必须是,它与文件中包含的字节无关。Ruby 不会为您自动检测编码。
force_encoding()
不会更改字符串中的字节,它只会更改附加到这些字节的编码。如果你告诉 Ruby“假装这个字符串是 ISO-8859-1”,那么当你告诉它“请把这个字符串写成 ISO-8859-1”时它不会对它们进行转码。 encode()
为您转码,如果您不欺骗它不这样做,则写入文件也是如此。
将它们放在一起,如果您有 ISO-8859-1 中的源文件:
# encoding: iso-8859-1
# Write in ISO-8859-1 regardless of default_external
File.open('foo.txt', "w:iso-8859-1") {|f| f << 'fòo'}
# Read in ISO-8859-1 regardless of default_external,
# transcoding if necessary to default_internal, if set
File.open('foo.txt', "r:iso-8859-1") {|f| puts f.read().encoding } # => ISO-8859-1
puts File.read('foo.txt').encoding # -> Whatever is specified by default_external
如果您有 UTF-8 格式的源文件:
# encoding: utf-8
# Write in ISO-8859-1 regardless of default_external, transcoding from UTF-8
File.open('foo.txt', "w:iso-8859-1") {|f| f << 'fòo'}
# Read in ISO-8859-1 regardless of default_external,
# transcoding if necessary to default_internal, if set
File.open('foo.txt', "r:iso-8859-1") {|f| puts f.read().encoding } # => ISO-8859-1
puts File.read('foo.txt').encoding # -> Whatever is specified by default_external
更新 2,回答您的新问题:
不,这# encoding: iso-8859-1
行没有改变Encoding.default_external
,它只是告诉 Ruby 源文件本身是用 ISO-8859-1 编码的。只需添加
Encoding.default_external = "iso-8859-1"
如果您希望您读取的所有文件都以该编码存储。
不,我个人认为 Ruby 不应该自动检测编码,但有理智的人可能不同意这一点,而且“应该这样”的讨论在这里似乎是题外话。
就个人而言,我对所有内容都使用 UTF-8,并且在我无法控制编码的极少数情况下,我在读取文件时手动设置编码,如上所示。我的源文件总是 UTF-8。如果您正在处理无法控制且不知道其编码的文件,则charguess gem或类似的会很有用。