如何将 Ruby 的 readlines.grep 用于 UTF-16 文件?
How to use Ruby's readlines.grep for UTF-16 files?
给定以下两个由以下命令创建的文件:
$ 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)
我尝试通过以下方式转换基于 this post 的文件:
$ 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
方法(它报告为未定义的方法).
如何对 UTF-16 文件使用 readlines.grep()
方法? 或者其他一些简单的方法,我的目标是打印具有特定正则表达式模式的行.
理想情况下在一行中,因此该命令可用于 CI 测试。
这是一些真实世界的场景:
ruby -e 'if File.readlines("utf-16.log").grep(/[1-9] error/) {exit 1}; end'
但由于日志文件的 UTF-16 格式,该命令不起作用。
简答:
你差不多搞定了,只需要说出你要替换哪些字符(我猜是无效的和未定义的):
$ 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 字符。
您可以通过在没有 force_encoding
的情况下调用 encode
来验证:
$ruby -e 'puts File.open("utf-16.txt", "r").read.encode("utf-8")'
��foo
bar
baz
黑框中的问号用于替换未知、无法识别或无法表示的字符。
您可以在 BOM 上查看更多 here。
虽然 Viktor 的回答在技术上是正确的,但将整个文件从 UTF-16LE
重新编码为 UTF-8
是不必要的,并且可能会影响性能。您真正需要的只是以相同的编码构建正则表达式:
puts File.open(
"utf-16.txt", mode: "rb:BOM|UTF-16LE"
).readlines.grep(
Regexp.new "foo".encode(Encoding::UTF_16LE)
)
#⇒ foo
给定以下两个由以下命令创建的文件:
$ 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)
我尝试通过以下方式转换基于 this post 的文件:
$ 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
方法(它报告为未定义的方法).
如何对 UTF-16 文件使用 readlines.grep()
方法? 或者其他一些简单的方法,我的目标是打印具有特定正则表达式模式的行.
理想情况下在一行中,因此该命令可用于 CI 测试。
这是一些真实世界的场景:
ruby -e 'if File.readlines("utf-16.log").grep(/[1-9] error/) {exit 1}; end'
但由于日志文件的 UTF-16 格式,该命令不起作用。
简答:
你差不多搞定了,只需要说出你要替换哪些字符(我猜是无效的和未定义的):
$ 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 字符。
您可以通过在没有 force_encoding
的情况下调用 encode
来验证:
$ruby -e 'puts File.open("utf-16.txt", "r").read.encode("utf-8")'
��foo
bar
baz
黑框中的问号用于替换未知、无法识别或无法表示的字符。
您可以在 BOM 上查看更多 here。
虽然 Viktor 的回答在技术上是正确的,但将整个文件从 UTF-16LE
重新编码为 UTF-8
是不必要的,并且可能会影响性能。您真正需要的只是以相同的编码构建正则表达式:
puts File.open(
"utf-16.txt", mode: "rb:BOM|UTF-16LE"
).readlines.grep(
Regexp.new "foo".encode(Encoding::UTF_16LE)
)
#⇒ foo