grep regex .* не соответствует всем

Я недавно стал использовать такие инструменты, как grep, wc, catи т. д., поскольку мне приходится иметь дело с некоторыми очень большими CSV-файлами (>10 ГБ), которые не совсем правильно разграничены (например, имеют вхождения символа разделителя внутри некоторых полей.

Работая с одним из этих файлов, я выполнил следующую команду, пытаясь найти способ правильно определить, какие экземпляры ; является разделителем и замените их другим символом:

grep -v -n --text "[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9].*[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9].*[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9].*[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]" < Transactions.csv

Регулярное выражение, вероятно, может быть сделано намного лучше, но в любом случае; удивительно то, что, помимо прочего, вышеприведенный код выводит следующую строку:

12345678:2016-10-25;12345678912345;2016-10-25;gobbledegook �IDNR: 69 ;12345.67;.00;2003-09-05;12345678;2003-09-03;stuff stuff         ;12345 fgadfkjgbsdkb;12/3/45678/9

(так как на самом деле это были данные транзакции, я изменил большинство значений полей, за исключением Может я глупый, но почему вышеприведенное регулярное выражение не соответствует этой строке? Похоже на регулярное выражение .* почему-то не соответствует этому персонажу по какой-то причине.

Я подозреваю, что файл сохраняется с использованием кодировки UTF-16, если это имеет какое-либо значение.

Редактировать: Спасибо @exore за ответ. Как оказалось, мой файл был закодирован в ISO-8859-15, который я смог вычислить grepвыкинуть строки, содержащие специальные символы, которых было относительно немного, в файл и открыть его в gedit. Я тогда использовал iconv преобразовать это в utf8, после чего он работал нормально!

1 ответ

Решение

Это типичная проблема кодирования символов. . означает любой персонаж. Но какая последовательность байтов является допустимым символом, зависит от кодировки. Работа с текстом без знания кодировки, безусловно, провал. Возможно, ваша команда grep ожидает строку в кодировке UTF-8. UTF-8 - это многобайтовая кодировка, означающая, что некоторые символы представлены несколькими байтами. Однако не все последовательности байтов являются действительными. См., Например, статью в Википедии о UTF-8.

Когда grep встречает последовательность байтов, которая не является допустимым символом в ожидаемой кодировке, он не может распознать его как символ, строка не совпадает, она выводится. Поскольку ваш терминал также не распознает символ, вы получаете ,

В вашем случае есть обходной путь. Скажите grep, чтобы он не беспокоился о кодировке, и рассмотрите один байт как один символ.

env LANG=C grep ....

или, может быть

env LANG=C LC_ALL=C grep ....

Вы можете легко проверить:

Создайте 2 файла, один в кодировке utf-8, один в кодировке utf-16-be:

$ echo éléphant | tee file.std | iconv -f utf8 -t utf16be >file.utf16be

Проверьте содержимое файлов:

$ cat file*
éléphant
�l�phant

Попробуй grep. Строка utf16be не распознана, нет вывода:

$ grep '^.*$' file*
file.std:éléphant

Не используйте кодирование вообще. Один байт - это один символ. все строки совпадают, просто означает, что терминал не распознает последовательность utf16be как действительный символ utf-8. Обратите внимание на использование -a чтобы сказать grep считать двоичным - это некоторый текст.

$ env LANG=C grep -a '^.*$' file*
file.std:éléphant
file.utf16be:�l�phant

Кроме того, если вы знаете кодировку, то вы можете использовать iconv чтобы сначала конвертировать ваш файл, затем используйте grep. Один из следующих должен работать.

iconv -f utf16   -t utf8 < file | grep ...
iconv -f utf16le -t utf8 < file | grep ...
iconv -f utf16be -t utf8 < file | grep ...
Другие вопросы по тегам