Как я grep для всех не-ASCII символов?


359

У меня есть несколько очень больших файлов XML, и я пытаюсь найти строки, которые содержат символы не ASCII. Я пробовал следующее:

grep -e "[\x{00FF}-\x{FFFF}]" file.xml

Но это возвращает каждую строку в файле, независимо от того, содержит ли строка символ в указанном диапазоне.

У меня неправильный синтаксис или я делаю что-то еще неправильно? Я также попробовал:

egrep "[\x{00FF}-\x{FFFF}]" file.xml 

(с одинарными и двойными кавычками вокруг шаблона).


Символы ASCII имеют длину только один байт, поэтому, если файл не является юникодом, не должно быть символов выше 0xFF.
— здав

Как мы пойдем выше \ xFF? Grep выдает ошибку «grep: диапазон не по порядку в классе символов».
— Mudit Jain

Ответы:


495

Вы можете использовать команду:

grep --color='auto' -P -n "[\x80-\xFF]" file.xml

Это даст вам номер строки и выделит символы не-ascii красным цветом.

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

grep --color='auto' -P -n "[^\x00-\x7F]" file.xml

Также обратите внимание, что важным битом является -Pфлаг, который соответствует --perl-regexp: поэтому он будет интерпретировать ваш шаблон как регулярное выражение Perl. Это также говорит о том, что

это очень экспериментально, и grep -P может предупредить о невыполненных функциях.


42
Это не будет работать в BSD grep(на OS X 10.8 Mountain Lion), так как он не поддерживает эту Pопцию.
— Бастиан М. ван де Вирд

20
Чтобы обновить мой последний комментарий, версия GNU grepдоступна в dupesбиблиотеке Homebrew (включите использование brew tap homebrew/dupes):brew install grep
— Бастиан М. ван де Веерд

48
@BastiaanVanDeWeerd правильно, grep в OSX 10.8 больше не поддерживает PCRE («Perl-совместимые регулярные выражения»), поскольку Дарвин теперь использует BSD grep вместо GNU grep. Альтернативой установке dupesбиблиотеки является установка pcreвместо: brew install pcre... как часть этого, вы получите pcregrepутилиту, которую вы можете использовать следующим образом:pcregrep --color='auto' -n "[\x80-\xFF]" file.xml
— pvandenberk

15
Для brewпользователей Mac можно установить coreutils из GNUbrew install coreutils . Это даст вам много инструментов GNU с префиксом «g» - в этом случае используйте ggrep. Это должно избежать проблем, возникающих при замене системной утилиты, поскольку системные скрипты Mac теперь зависят от BSD grep.
— Джоэл Пурра

22
это отлично работает на Mac, ag "[\x80-\xFF]" fileвам просто нужно установитьthe_silver_searcher
— slf

123

Вместо того, чтобы делать предположения о диапазоне байтов не-ASCII-символов, как это делает большинство вышеупомянутых решений, немного лучше, чтобы IMO явно указывал фактический диапазон байтов ASCII-символов.

Таким образом, первое решение, например, станет:

grep --color='auto' -P -n '[^\x00-\x7F]' file.xml

(который в основном greps для любого символа вне шестнадцатеричного диапазона ASCII: от \ x00 до \ x7F)

На Mountain Lion это не будет работать (из-за отсутствия поддержки PCRE в BSD grep) , но с pcreустановленной через Homebrew, следующее будет работать также:

pcregrep --color='auto' -n '[^\x00-\x7F]' file.xml

Есть плюсы или минусы, которые кто-нибудь может придумать?


9
Это на самом деле сработало для меня, где вышеперечисленные решения не удалось. Найти апострофы M $ Word еще проще!
— AlbertEngelB

2
Если у вас есть bash-совместимая оболочка, но не работает pcre-grep, она LC_COLLATE=C grep $'[^\1-\177]'работает (для файлов без нулевых байтов)
— idupree

2
Это решение работает более последовательно, чем приведенные выше.
— 0xcaff

1
Мне пришлось использовать это, чтобы забрать кандзи, кириллицу и традиционный китайский в моем файле UTF8, используя «[\ x80- \ xFF]», пропустив все это.
— buckaroo1177125

1
Профи это работало превосходно, в то время как другие варианты были великолепны, но не так хороши. Никаких минусов пока не найдено.
— jwpfox 19.09.16

67

Следующие работы для меня:

grep -P "[\x80-\xFF]" file.xml

Не-ASCII символы начинаются с 0x80 и переходят в 0xFF при просмотре байтов. Grep (и семейство) не выполняют обработку Unicode, чтобы объединить многобайтовые символы в одну сущность для соответствия регулярному выражению, как вам кажется. -PВариант в моем Grep позволяет использовать \xddпобеги в классах символов , чтобы выполнить то , что вы хотите.


1
Для представления, которое может не сразу знать, как вызвать это для нескольких файлов, просто запустите: find. имя * .xml | xargs grep -P "[\ x80- \ xFF]"
— Дэвид Мохундро

1
Это возвращает совпадение, но нет никаких указаний на то, что это за персонаж и где он находится. Как увидеть, что это за персонаж и где он?
— Фахим Митха

Добавление «-n» даст номер строки, кроме того, невидимые символы будут отображаться в виде блока в терминале: grep -n -P «[\ x80- \ xFF]» file.xml
— fooMonster

4
У меня проблема с Hangul Korean: echo '소녀시대' | grep -P "[\x80-\xFF]"для меня ничего не возвращается - кто-нибудь еще может подтвердить? (GNU grep 2.21)
— безумно

@frabjous же здесь, но обратное оглавление работы: echo '소녀시대' | grep -P "[^\x00-\x7F]". Или просто используйте the_silver_searcherкак указано @slf:echo '소녀시대' | ag "[\x80-\xFF]"
— Псмит

55

В перл

perl -ane '{ if(m/[[:^ascii:]]/) { print  } }' fileName > newFile

1
На OSX10.11 мне пришлось попробовать несколько решений grep + regex, прежде чем я нашел это, которое действительно работает
— sg

Хотите поделиться этим OSX решением @sg ?!
— геоэтерия

Приведенный выше скрипт на Perl - это решение, о котором я говорю
— sg

5
perl -lne 'print if /[^[:ascii:]]/' file.xml
— Naveed

43

Самый простой способ - определить не-ASCII-символ ... как символ, не являющийся ASCII-символом.

LC_ALL=C grep '[^ -~]' file.xml

Добавьте вкладку после ^ если это необходимо.

Параметр LC_COLLATE=Cпозволяет избежать неприятных сюрпризов о значении диапазонов символов во многих локалях. Установка LC_CTYPE=Cнеобходима для соответствия однобайтовых символов - в противном случае команда пропустит недопустимые последовательности байтов в текущей кодировке. Настройка полностью LC_ALL=Cисключает зависящие от локали эффекты.


На RedHat 6.4 с tcsh мне пришлось использовать <<< env LC_COLLATE = C grep -n '[^ - ~]' file.xml >>>. Я добавил -n, чтобы получить номер строки.
— ddevienne

Для меня echo "A" | LC_COLLATE=C grep '[^ -~]'возвращается матч
— frabjous

1
@frabjous Если у вас есть LC_ALL=en_US.UTF-8, это превосходит LC_COLLATEнастройки. Вы не должны иметь это в своей среде! LC_ALLобычно заставляет определенную задачу использовать конкретную локаль C. Чтобы установить язык по умолчанию для всех категорий, установите LANG.
— Жиль "ТАК - перестань быть злым"

1
Сначала я не добавлял LC_ALL=C, это ведет себя по-разному в Mac OS X и Ubuntu. После того, как я добавлю этот параметр, они дают тот же результат.
— Макс Пэн

1
Это работает на Mac, а другие решения на основе grep - нет.
— Матиас Фрипп

26

Вот еще один вариант, который я обнаружил, который дал совершенно разные результаты поиска grep [\x80-\xFF]в принятом ответе. Возможно, кому-то будет полезно найти дополнительные символы, не относящиеся к ascii:

grep --color='auto' -P -n "[^[:ascii:]]" myfile.txt

Примечание: у grep моего компьютера (Mac) не было -Pопции, поэтому я сделал brew install grepи начал вызов выше с ggrepвместо grep.


2
Это, безусловно, лучший ответ, поскольку он работает как для Mac, так и для Linux.
— tommy.carstensen

Только тот, который работал для меня на Linux.

9

Следующий код работает:

find /tmp | perl -ne 'print if /[^[:ascii:]]/'

Замените /tmpна имя каталога, который вы хотите найти.


2
На Mac это работает, а большинство основанных на grep - нет.
— Матиас Фрипп

9

Поиск непечатаемых символов. TLDR; Управляющее резюме

  1. поиск контрольных символов и расширенного Unicode
  2. настройка локали, например, LC_ALL=Cнеобходимая, чтобы grep делал то, что вы могли ожидать с расширенным юникодом

Так что предпочтительные не ASCII Char Finders:

$ perl -ne 'print "$. $_" if m/[\x00-\x08\x0E-\x1F\x80-\xFF]/' notes_unicode_emoji_test

как в верхнем ответе, обратный grep:

$ grep --color='auto' -P -n "[^\x00-\x7F]" notes_unicode_emoji_test

как в верхнем ответе, но с LC_ALL=C:

$ LC_ALL=C grep --color='auto' -P -n "[\x80-\xFF]" notes_unicode_emoji_test

, , Больше . , Мучительная деталь в этом: , ,

Я согласен с Харви выше, погруженным в комментарии, часто полезнее искать непечатные символы ИЛИ легко думать не ASCII, когда вы действительно должны думать, что вы не печатаете. Харви предлагает "использовать это:" [^\n -~]". Добавьте \ r для текстовых файлов DOS. Это означает" [^\x0A\x020-\x07E]"и добавьте \ x0D для CR"

Кроме того, добавление -c (показать количество совпавших шаблонов) в grep полезно при поиске непечатаемых символов, поскольку соответствующие строки могут испортить терминал.

Я обнаружил, что добавление диапазона 0-8 и 0x0e-0x1f (к диапазону 0x80-0xff) является полезным шаблоном. Это исключает TAB, CR и LF и один или два необычных печатных символа. ИМХО, довольно полезный (хотя и грубый) шаблон grep это:

grep -c -P -n "[\x00-\x08\x0E-\x1F\x80-\xFF]" *

В действительности, как правило, вам нужно сделать это:

LC_ALL=C grep -c -P -n "[\x00-\x08\x0E-\x1F\x80-\xFF]" *

сломать:

LC_ALL=C - set locale to C, otherwise many extended chars will not match (even though they look like they are encoded > 0x80)
\x00-\x08 - non-printable control chars 0 - 7 decimal
\x0E-\x1F - more non-printable control chars 14 - 31 decimal
\x80-1xFF - non-printable chars > 128 decimal
-c - print count of matching lines instead of lines
-P - perl style regexps

Instead of -c you may prefer to use -n (and optionally -b) or -l
-n, --line-number
-b, --byte-offset
-l, --files-with-matches

Например, практический пример использования find для поиска всех файлов в текущем каталоге:

LC_ALL=C find . -type f -exec grep -c -P -n "[\x00-\x08\x0E-\x1F\x80-\xFF]" {} + 

Вы можете настроить grep время от времени. например, BS (0x08 - backspace) char, используемый в некоторых печатаемых файлах или для исключения VT (0x0B - вертикальная вкладка). Символы BEL (0x07) и ESC (0x1B) также могут считаться пригодными для печати в некоторых случаях.

Non-Printable ASCII Chars
** marks PRINTABLE but CONTROL chars that is useful to exclude sometimes
Dec   Hex Ctrl Char description           Dec Hex Ctrl Char description
0     00  ^@  NULL                        16  10  ^P  DATA LINK ESCAPE (DLE)
1     01  ^A  START OF HEADING (SOH)      17  11  ^Q  DEVICE CONTROL 1 (DC1)
2     02  ^B  START OF TEXT (STX)         18  12  ^R  DEVICE CONTROL 2 (DC2)
3     03  ^C  END OF TEXT (ETX)           19  13  ^S  DEVICE CONTROL 3 (DC3)
4     04  ^D  END OF TRANSMISSION (EOT)   20  14  ^T  DEVICE CONTROL 4 (DC4)
5     05  ^E  END OF QUERY (ENQ)          21  15  ^U  NEGATIVE ACKNOWLEDGEMENT (NAK)
6     06  ^F  ACKNOWLEDGE (ACK)           22  16  ^V  SYNCHRONIZE (SYN)
7     07  ^G  BEEP (BEL)                  23  17  ^W  END OF TRANSMISSION BLOCK (ETB)
8     08  ^H  BACKSPACE (BS)**            24  18  ^X  CANCEL (CAN)
9     09  ^I  HORIZONTAL TAB (HT)**       25  19  ^Y  END OF MEDIUM (EM)
10    0A  ^J  LINE FEED (LF)**            26  1A  ^Z  SUBSTITUTE (SUB)
11    0B  ^K  VERTICAL TAB (VT)**         27  1B  ^[  ESCAPE (ESC)
12    0C  ^L  FF (FORM FEED)**            28  1C  ^\  FILE SEPARATOR (FS) RIGHT ARROW
13    0D  ^M  CR (CARRIAGE RETURN)**      29  1D  ^]  GROUP SEPARATOR (GS) LEFT ARROW
14    0E  ^N  SO (SHIFT OUT)              30  1E  ^^  RECORD SEPARATOR (RS) UP ARROW
15    0F  ^O  SI (SHIFT IN)               31  1F  ^_  UNIT SEPARATOR (US) DOWN ARROW

ОБНОВЛЕНИЕ: я должен был вернуться к этому недавно. И, YYMV в зависимости от настроек терминала / прогноза солнечной погоды, НО. , Я заметил, что grep не находил много юникодов или расширенных символов. Хотя интуитивно они должны соответствовать диапазону от 0x80 до 0xff, 3 и 4-байтовые символы Unicode не были сопоставлены. ??? Кто-нибудь может объяснить это? ДА. @frabjous спросил и @calandoa объяснил, что LC_ALL=Cследует использовать, чтобы установить языковой стандарт для команды, чтобы сделать совпадение grep.

например, мой язык LC_ALL=пуст

$ locale
LANG=en_IE.UTF-8
LC_CTYPE="en_IE.UTF-8"
.
.
LC_ALL=

grep с LC_ALL=пустыми совпадениями 2-байтовые кодированные символы, но не 3 и 4-байтовые:

$ grep -P -n "[\x00-\x08\x0E-\x1F\x80-\xFF]" notes_unicode_emoji_test
5:© copyright c2a9
7:call  underscore c2a0
9:CTRL
31:5 © copyright
32:7 call  underscore

grep с LC_ALL=Cдействительно соответствует всем расширенным символам, которые вы хотели бы:

$ LC_ALL=C grep --color='auto' -P -n "[\x80-\xFF]" notes_unicode_emoji_test  
1:���� unicode dashes e28090
3:��� Heart With Arrow Emoji - Emojipedia == UTF8? f09f9298
5:� copyright c2a9
7:call� underscore c2a0
11:LIVE��E! ���������� ���� ���������� ���� �� �� ���� ����  YEOW, mix of japanese and chars from other e38182 e38184 . . e0a487
29:1 ���� unicode dashes
30:3 ��� Heart With Arrow Emoji - Emojipedia == UTF8 e28090
31:5 � copyright
32:7 call� underscore
33:11 LIVE��E! ���������� ���� ���������� ���� �� �� ���� ����  YEOW, mix of japanese and chars from other
34:52 LIVE��E! ���������� ���� ���������� ���� �� �� ���� ����  YEOW, mix of japanese and chars from other
81:LIVE��E! ���������� ���� ���������� ���� �� �� ���� ����  YEOW, mix of japanese and chars from other

НАСТОЯЩЕЕ соответствие perl (частично найденное в другом месте в stackoverflow) ИЛИ обратный grep в верхнем ответе, похоже, обнаруживает ВСЕ странные ~ и ~ чудесные ~ "не-ascii" символы без установки языкового стандарта:

$ grep --color='auto' -P -n "[^\x00-\x7F]" notes_unicode_emoji_test

$ perl -ne 'print "$. $_" if m/[\x00-\x08\x0E-\x1F\x80-\xFF]/' notes_unicode_emoji_test  

1 ‐‐ unicode dashes e28090
3 💘 Heart With Arrow Emoji - Emojipedia == UTF8? f09f9298
5 © copyright c2a9
7 call  underscore c2a0
9 CTRL-H CHARS URK URK URK 
11 LIVE‐E! あいうえお かが アイウエオ カガ ᚊ ᚋ ซฌ आइ  YEOW, mix of japanese and chars from other e38182 e38184 . . e0a487
29 1 ‐‐ unicode dashes
30 3 💘 Heart With Arrow Emoji - Emojipedia == UTF8 e28090
31 5 © copyright
32 7 call  underscore
33 11 LIVE‐E! あいうえお かが アイウエオ カガ ᚊ ᚋ ซฌ आइ  YEOW, mix of japanese and chars from other
34 52 LIVE‐E! あいうえお かが アイウエオ カガ ᚊ ᚋ ซฌ आइ  YEOW, mix of japanese and chars from other
73 LIVE‐E! あいうえお かが アイウエオ カガ ᚊ ᚋ ซฌ आइ  YEOW, mix of japanese and chars from other

Так что предпочтительные не ASCII Char Finders:

$ perl -ne 'print "$. $_" if m/[\x00-\x08\x0E-\x1F\x80-\xFF]/' notes_unicode_emoji_test

как в верхнем ответе, обратный grep:

$ grep --color='auto' -P -n "[^\x00-\x7F]" notes_unicode_emoji_test

как в верхнем ответе, но с LC_ALL=C:

$ LC_ALL=C grep --color='auto' -P -n "[\x80-\xFF]" notes_unicode_emoji_test

1
Ответьте, почему grep не соответствует символам, закодированным более чем в 2 байтах, благодаря @calandoa и frabjous в комментариях выше к вопросу. Используйте LC_ALL = C перед командой grep.
— gaoithe

1
Большое спасибо за то, что потрудились опубликовать ответ, похороненный под 800 другими возражениями! Моя проблема была 0x02 символ. Возможно, вы захотите поместить этот «практический пример использования» в самый верх, так как вам действительно не нужно читать весь пост, чтобы просто увидеть, является ли это вашей проблемой.
— Ноумен

1
Я знаю, действительно старый ответ, и мучительные детали, но исправить, полезно для меня и других, также я надеюсь. Вы правы, я добавил TLDR; наверху
— Гаоита

1

Странно, я должен был сделать это сегодня! Я закончил тем, что использовал Perl, потому что я не мог заставить работать grep / egrep (даже в режиме -P). Что-то вроде:

cat blah | perl -en '/\xCA\xFE\xBA\xBE/ && print "found"'

Для символов Юникода (например, \u2212 в примере ниже) используйте это:

find . ... -exec perl -CA -e '$ARGV = @ARGV[0]; open IN, $ARGV; binmode(IN, ":utf8"); binmode(STDOUT, ":utf8"); while (<IN>) { next unless /\N{U+2212}/; print "$ARGV: $&: $_"; exit }' '{}' \;

1

Было бы интересно узнать, как искать один символ Юникода. Эта команда может помочь. Вам нужно только знать код в UTF8

grep -v $'\u200d'

Я на самом деле не эксперт, но я знаю достаточно, чтобы понять, что это не представление UTF8, это UTF16, или, может быть, UTF32 или UCS16. Для двухбайтовой кодовой точки эти три могут быть одинаковыми.
— Baxissimo

1

Поиск всех символов, отличных от ascii, создает впечатление, что кто-то ищет строки в юникоде или намеревается раздеть эти символы по отдельности.

Для первого, попробуйте один из них (переменная fileиспользуется для автоматизации):

 file=file.txt ; LC_ALL=C grep -Piao '[\x80-\xFF\x20]{7,}' $file | iconv -f $(uchardet $file) -t utf-8

 file=file.txt ; pcregrep -iao '[\x80-\xFF\x20]{7,}' $file | iconv -f $(uchardet $file) -t utf-8

 file=file.txt ; pcregrep -iao '[^\x00-\x19\x21-\x7F]{7,}' $file | iconv -f $(uchardet $file) -t utf-8

Ванильный grep не работает правильно без LC_ALL = C, как отмечалось в предыдущих ответах.

Диапазон ASCII есть x00-x7F, пространствоx20 , так как в строках есть пробелы, которые отрицательный диапазон пропускает.

Диапазон не ASCII x80-xFF, так как строки имеют пробелы, положительный диапазон добавляет его.

Предполагается, что строка должна содержать не менее 7 последовательных символов в диапазоне. {7,},

Для вывода, читаемого оболочкой, uchardet $fileвозвращает предположение о кодировке файла, которая передается в iconv для автоматической интерполяции.


Это очень полезно из-за упоминания uchardetкоманды. Спасибо за это хедз-ап!
— bballdave025
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.