1

给定以下命令创建的以下两个文件:

$ printf "foo\nbar\nbaz\n" | iconv -t UTF-8 > utf-8.txt
$ printf "foo\nbar\nbaz\n" | iconv -t UTF-16 > utf-16.txt
$ file utf-8.txt utf-16.txt
utf-8.txt:  ASCII text
utf-16.txt: Little-endian UTF-16 Unicode text

我想在 UTF-16 格式的文件中找到匹配的模式,就像在 UTF-8 中使用 Ruby 一样。

这是 UTF-8 文件的工作示例:

$ ruby -e 'puts File.open("utf-8.txt").readlines.grep(/foo/)'
foo

但是,它不适用于 UTF-16LE 格式的文件:

$ ruby -e 'puts File.open("utf-16.txt").readlines.grep(/foo/)'
Traceback (most recent call last):
    3: from -e:1:in `<main>'
    2: from -e:1:in `grep'
    1: from -e:1:in `each'
-e:1:in `===': invalid byte sequence in US-ASCII (ArgumentError)

我尝试通过以下方式转换基于此帖子的文件:

$ ruby -e 'puts File.open("utf-16.txt", "r").read.force_encoding("ISO-8859-1").encode("utf-8", replace: nil)' 
ÿþfoo
bar
baz

但它ÿþ之前打印了一些无效字符()foo,其次我不知道grep转换后如何使用方法(它报告为未定义的方法)。

如何使用readlines.grep()UTF-16 文件的方法?或者其他一些简单的方法,我的目标是使用特定的正则表达式模式打印行。


理想情况下在一行中,因此该命令可用于 CI 测试。

这是一些真实世界的场景:

ruby -e 'if File.readlines("utf-16.log").grep(/[1-9] error/) {exit 1}; end'

但由于日志文件的 UTF-16 格式,该命令不起作用。

4

2 回答 2

2

简短的回答:

您几乎拥有它,只需要说出您要替换哪些字符(我猜是无效和未定义的):

$ ruby -e 'puts File.open("utf-16.txt", "r").read.encode("UTF-8", invalid: :replace, undef: :replace, replace: "")'
foo
bar
baz

另外我认为你不需要force_encoding.

如果您想BOM在打开和使用时忽略转换,readlines您可以使用:

 ruby -e 'puts File.open("utf-16.txt", mode: "rb:BOM|UTF-16LE:UTF-8").readlines.grep(/foo/)'

更多细节:

执行此操作时出现无效字符的原因:

$ruby -e 'puts File.open("utf-16.txt", "r").read.force_encoding("ISO-8859-1").encode("utf-8", replace: nil)'
ÿþfoo
bar
baz

是在每个 Unicode 文件的开头,您可以有显示字节顺序和编码形式的字节顺序标记。在您的情况下,它是FE FF(意思是 Little-endian UTF-16),它们是无效的 UTF-8 字符。

encode您可以通过不调用来验证force_encoding

$ruby -e 'puts File.open("utf-16.txt", "r").read.encode("utf-8")'
��foo
bar
baz

黑框中的问号用于替换未知、无法识别或无法表示的字符。

您可以在此处查看更多关于 BOM的信息。

于 2019-02-17T01:57:49.777 回答
2

虽然 Viktor 的回答在技术上是正确的,但将整个文件从UTF-16LEinto重新编码UTF-8是不必要的,并且可能会影响性能。您真正需要的只是以相同的编码构建正则表达式:

puts File.open(
  "utf-16.txt", mode: "rb:BOM|UTF-16LE"
).readlines.grep(
  Regexp.new "foo".encode(Encoding::UTF_16LE)
)
#⇒ foo
于 2019-02-17T05:19:07.773 回答