在Windows上,Ruby 1.9文件编码错误

3

我有一个包含以下内容的Ruby文件:

# encoding: iso-8859-1
File.open('foo.txt', "w:iso-8859-1") {|f| f << 'fòo'}
puts File.read('foo.txt').encoding
  • 当我在Windows命令提示符下运行ruby 1.9.3时,我会得到: IBM437
  • 当我在cygwin ruby 1.9.3下运行它时,我会得到: UTF-8
  • 我希望得到的是: iso-8859-1

有人能解释一下这里发生了什么吗?

更新

这是我正在寻求的更好描述:

  • 现在我明白了,感谢Darshan的解释,默认情况下ruby将在Encoding.default_external中加载文件,但是# encoding:iso-8859-1这一行不应该覆盖它吗?
  • ruby能够自动检测文件的编码吗?是否有任何文件系统其编码是属性?
  • 我最好的选择是“记住”我保存文件的编码吗?
1个回答

7
您读取文件时没有指定编码方式。您很小心地在各个地方都指定了编码方式,但是在这里却使用默认编码方式进行读取。
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')。后者保留了字节不变,而前者对字符串进行了转码。
更新:我会详细解释一下,因为我没有像我本应该那样清晰和全面地说明。
  1. 如果你没有使用File.read()指定编码,文件将使用Encoding.default_external读取。由于你没有自己设置,Ruby将使用依赖于其运行环境的值。在Windows环境中,它是IBM437;在Cygwin环境中,它是UTF-8。所以我的观点是,当然这就是编码;它必须是,这与文件中包含的字节无关。Ruby不会自动检测编码。
  2. 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,回答您的新问题:

  1. No, the # encoding: iso-8859-1 line does not change Encoding.default_external, it only tells Ruby that the source file itself is encoded in ISO-8859-1. Simply add

    Encoding.default_external = "iso-8859-1"
    

    if you expect all files that your read to be stored in that encoding.

  2. No, I don't personally think Ruby should auto-detect encodings, but reasonable people can disagree on that one, and a discussion of "should it be so" seems off-topic here.

  3. Personally, I use UTF-8 for everything, and in the rare circumstances that I can't control encoding, I manually set the encoding when I read the file, as demonstrated above. My source files are always in UTF-8. If you're dealing with files that you can't control and don't know the encoding of, the charguess gem or similar would be useful.


我正在尝试证明我正在将一个 iso-8859-1 字符串写入文件,但是当我读取文件内容时编码是错误的。假设我不仅仅是写入该文件,而且我也不知道它的编码。 - pguardiario
是的,关于Encoding.default_external的评论很有见地,但让我们忘记force_encoding吧,它与问题无关。我将更新更好的描述来表达我的需求。 - pguardiario
@pguardiario 好的,我回答了新问题。 - Darshan Rivka Whittle
我决定使用Marshal#dump/load将字符串写入文件,这似乎是确保编码正确的最佳方法。 - pguardiario
@pguardiario 这是一个好主意,一种将元数据与数据存储在一起的简单方式。 - Darshan Rivka Whittle
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接