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 ...