在读取时擦除错误编码的字节序列

问题描述 投票:0回答:1

我正在将文件读入 Ruby 字符串,这些字符串稍后会被进一步处理(例如,使用 CSV 模块)。文件的外部编码是一个参数,据推测,要处理的文件应该是指定的编码。

在阅读过程中,我将文件从假定的外部编码转换为 UTF-8。

有时,我会收到错误的文件,这些文件的编码方式与指定的编码不同。

当然,如果编码错误,我的程序只会读取垃圾,但是如果编码不仅错误,而且甚至包含在假设的编码下非法的字节序列,我在处理文件时会得到异常。

规范要求那些由于编码不正确而无法解密的字节序列应该从输入文件中简单地删除,而不是导致程序中止。

为了实现这一点,我将文件读入如下字符串:

 UTF8_CONVERTER = ->(field) { field.encode('utf-8', invalid: :replace, undef: :replace, replace: "") }

read_flags = {
   external_encoding: ext_enc, # i.e. Encoding::ISO_8859_1
   internal_encoding: Encoding::UTF_8,
   converters: UTF8_CONVERTER
}

file_content = IO.read(file_path, read_flags)

IMO,这应该使 file_content 成为 UTF-8 编码的有效字符串。如果我的程序稍后决定应该对该字符串进行 CSV 解析,它会像这样调用 csv 解析器:

e_enc = file_content.encoding
i_enc = Encoding::UTF_8
...
csv_opt = { col_sep: ';', row_sep: :auto, external_encoding: e_enc, internal_encoding: i_enc}
CSV.foreach(file_content, csv_opt) { .... }

我在这里也多余地指定编码的原因是,处理 CSV 的方法具有通用目的,并且如果字符串具有不同的编码也应该起作用。

但是,这不起作用:

如果我正在处理一个应该是UTF-8的文件(即

ext_enc
等于
Encoding::UTF_8
),但实际上是在Windows-1252中编码的,并且其中有一些字节序列,这将是在 UTF 下非法,
CSV.foreach
引发异常 ArgumentError: UTF-8 中的无效字节序列

我由此得出结论,我的

UTF8_CONVERTER
没有删除不正确的字节。

有人能看到我在这里做错了什么吗?

更新

@Stefan 在评论中指出,

converter
选项不能在
IO.read
中使用,并建议我直接传递转换选项。这也不起作用(我必须使用 JRuby 1.7.21,它相当于 Ruby 1.9.3)。我至少可以创建一个小的、可重现的示例:

我创建一个文件

illegal.txt
,其中包含以下内容:

> xxd illegal.txt
00000000: 66fc 720a                                f.r.

我们可以看到它包含字节序列FC 72,这是不合法的UTF-8。

现在我用

读取文件
fc=IO.read('illegal.txt', {:external_encoding=>#<Encoding:UTF-8>, :internal_encoding=>#<Encoding:UTF-8>, :invalid=>:replace, :undef=>:replace, :replace=>""}

我预计,这至少会删除

FC
,因此生成的字符串将是
"fr\n"
,或者可能只是
"f\n"
。然而,当我做一个

puts fc.bytes.to_a

我仍然看到

[102, 252, 114, 10]
打印。

ruby encoding
1个回答
0
投票

我找到了一个解决方案,但它太丑陋了,我希望有人想出更好的主意:

根据我问题中的示例代码,我已将文件

illegal.txt
的内容重新放入字符串变量
fc
中,其中仍然包含非法字节序列
FC 72

要删除有问题的 FC,我可以执行以下操作

fc.force_encoding(Encoding::ASCII_8BIT)
fc_fixed = fc.encode(Encoding::UTF_8, undef: :replace, replace: '')

此后,

puts fc_fixed.bytes.to_a
显示
102, 114, 10]

需要额外的

force_encoding
步骤,因为
fc
已经编码 UTF-8,并且至少在我的 Ruby 版本中,
fc.encode
会简单地将所有内容保留在原处。

© www.soinside.com 2019 - 2024. All rights reserved.