Як я переглядаю всі символи, що не належать до ASCII?


359

У мене є кілька дуже великих XML-файлів, і я намагаюся знайти рядки, що містять символи, що не належать до ASCII. Я спробував таке:

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

Але це повертає кожен рядок у файлі, незалежно від того, чи містить рядок символ у вказаному діапазоні.

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

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

(з одинарними та подвійними лапками навколо візерунка).


Символи ASCII мають лише один байт, тому, якщо файл не є однокодовим, символів вище 0xFF не повинно бути.
zdav

Як ми переходимо вище \ xFF? Grep дає помилку "grep: діапазон поза порядком у класі символів".
Mudit Jain

Відповіді:


493

Ви можете використовувати команду:

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

Це дасть вам номер рядка та виділить символи, що не належать ascii, червоним кольором.

У деяких системах, залежно від ваших налаштувань, вищезгадане не працюватиме, тож ви можете перехопити зворотне

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

Зауважте також, що важливим бітом є -Pпрапор, який прирівнюється до --perl-regexp: так він буде інтерпретувати ваш візерунок як регулярний вираз Perl. Це також говорить про це

це дуже експериментально, а греп -P може попередити про бездоганні функції.


42
Це не буде працювати в BSD grep(на OS X 10.8 Mountain Lion), оскільки він не підтримує цю Pопцію.
Бастіян М. ван де Веерд

20
Щоб оновити мій останній коментар, версія GNU grepдоступна в dupesбібліотеці Homebrew (увімкніть використання brew tap homebrew/dupes):brew install grep
Bastiaan M. van de Weerd

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 можуть бути встановлені основні програми 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

(що в основному відображається за будь-який символ поза шістнадцятковим діапазоном 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

67

Наступні роботи для мене:

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

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


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

1
Це повертає відповідність, але немає вказівки того, що є персонажем і де він знаходиться. Як можна бачити, що це за персонаж, і де він?
Faheem Mitha

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

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

@frabjous ж тут, але зворотне зміст роботи: echo '소녀시대' | grep -P "[^\x00-\x7F]". Або просто використовувати, the_silver_searcherяк вказувало @slf:echo '소녀시대' | ag "[\x80-\xFF]"
psmith

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, в той час як інші рішення на базі грепу - ні.
Маттіас Фріпп

26

Ось ще один варіант, який я виявив, що дав абсолютно різні результати пошуку грепа [\x80-\xFF]в прийнятій відповіді. Можливо, комусь буде корисно знайти додаткових символів, які не мають права:

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

Примітка: у грепа на моєму комп’ютері (для Mac) не було -Pможливості, тому я зробив brew install grepі запустив виклик вище, а ggrepне з grep.


2
Це, безумовно, найкраща відповідь, оскільки він працює як для Mac, так і для Linux.
tommy.carstensen

Тільки той, хто працював на мене в Linux.

9

Наступний код працює:

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

Замініть /tmpім'я каталогу, який ви хочете шукати.


2
На Mac це працює, в той час як більшість тих, хто базується на грепах, це не так.
Маттіас Фріпп

9

Пошук недрукованих символів. TLDR; Резюме

  1. пошук символів управління І розширеного unicode
  2. Налаштування локалі, наприклад, LC_ALL=Cнеобхідна для того, щоб grep робив те, що ви могли очікувати при розширеному unicode

Отож, кращі шукачі не-ассі:

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

як у верхній відповіді, зворотний греп:

$ 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

. . більше. . хвилююча деталь щодо цього:. . .

Я погоджуюся з вищезгаданим у коментарях Харві , часто корисніше шукати символи, що не друкуються. Харві пропонує "використовувати це:" [^\n -~]". Додайте текстові файли DOS. Це означає"[^\x0A\x020-\x07E] "і додайте \ x0D для CR"

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

Я виявив, що додавання діапазону 0-8 та 0x0e-0x1f (до діапазону 0x80-0xff) є корисною схемою. Це виключає TAB, CR та LF та ще одну або дві незвичайні символи для друку. Таким чином, IMHO є досить корисним (хоч і неочищеним) шаблоном греп-файлів:

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]" {} + 

Можливо, ви хочете часом регулювати грейп. наприклад, BS (0x08 - зворотний простір), який використовується в деяких файлах для друку або для виключення 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 with 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) АБО зворотний греп у верхній відповіді НЕ, здається, знайде ВСІ ~ дивні ~ та ~ чудові ~ "non-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 LIVEE! あいうえお かが アイウエオ カガ   ซฌ आइ  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 LIVEE! あいうえお かが アイウエオ カガ   ซฌ आइ  YEOW, mix of japanese and chars from other
34 52 LIVEE! あいうえお かが アイウエオ カガ   ซฌ आइ  YEOW, mix of japanese and chars from other
73 LIVEE! あいうえお かが アイウエオ カガ   ซฌ आइ  YEOW, mix of japanese and chars from other

Отож, кращі шукачі не-ассі:

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

як у верхній відповіді, зворотний греп:

$ 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 у коментарях, що викладені вище. Перед командою grep використовуйте LC_ALL = C.
gaoithe

1
Велике спасибі за те, що потурбувались опублікувати відповідь, похований під 800 іншими оновленнями! Моя проблема була символом 0x02. Ви можете поставити цей "практичний приклад використання" біля верхньої частини, оскільки вам справді не потрібно читати весь пост, щоб просто побачити, чи це ваша проблема.
Ноумен

1
Я знаю, справді стара відповідь і хвилююча деталь, але правильна корисна для мене та інших також сподіваюся. Ви маєте рацію, я додав TLDR; вгорі.
gaoithe

1

Як не дивно, мені довелося це зробити сьогодні! Я в кінцевому рахунку використовував Perl, тому що я не міг змусити grep / egrep працювати (навіть у режимі -P). Щось на зразок:

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

Для символів unicode (як \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

Це може бути цікаво знати, як шукати одного символу unicode. Ця команда може допомогти. Потрібно знати лише код у UTF8

grep -v $'\u200d'

Я насправді не експерт, але я знаю достатньо, щоб знати, що це не представлення UTF8, це UTF16 або, можливо, UTF32 або UCS16. Для 2-байтової кодової точки ці три можуть бути однаковими.
Baxissimo

1

Знаходження всіх символів, що не відносяться до ascii, створює враження, що або шукає рядки Unicode, або має намір викреслити вказані символи окремо.

Для перших спробуйте один із них (змінна 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

Ванільний греп не працює правильно без 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.