Как использовать Ruby's readlines.grep для файлов UTF-16? - PullRequest
0 голосов
/ 17 февраля 2019

Учитывая следующие два файла, созданные с помощью следующих команд:

$ 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 форматирования файла журнала.

Ответы [ 2 ]

0 голосов
/ 17 февраля 2019

Хотя ответ Виктора технически верен, перекодирование всего файла из 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
0 голосов
/ 17 февраля 2019

Краткий ответ:

У вас почти есть это, просто нужно сказать, какие символы вы хотите заменить (я бы предположил, недопустимые и неопределенные):

$ 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 (имеется в виду UTF-16 с прямым порядком байтов), которые являются недопустимыми символами UTF-8.

Это можно проверить, вызвав encode без force_encoding:

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

Знаки вопроса в черном квадрате используются для замены неизвестного, нераспознанного или непредставимого символа.

Подробнее о спецификации можно прочитать здесь .

...