Bagaimana cara saya menerima semua karakter non-ASCII?

359

Saya memiliki beberapa file XML yang sangat besar dan saya mencoba menemukan garis yang berisi karakter non-ASCII. Saya sudah mencoba yang berikut ini:

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

Tapi ini mengembalikan setiap baris dalam file, terlepas dari apakah baris tersebut mengandung karakter dalam rentang yang ditentukan.

Apakah saya salah sintaks atau saya melakukan sesuatu yang salah? Saya juga sudah mencoba:

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

(dengan tanda kutip tunggal dan ganda di sekitar pola).

pconrey
sumber
Karakter ASCII hanya satu byte panjangnya, jadi kecuali file tersebut adalah unicode, tidak boleh ada karakter di atas 0xFF.
zdav
Bagaimana cara kita pergi di atas \ xFF? Grep memberikan kesalahan "grep: range out of order in class character".
Mudit Jain

Jawaban:

494

Anda dapat menggunakan perintah:

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

Ini akan memberi Anda nomor baris, dan akan menyoroti karakter non-ascii berwarna merah.

Dalam beberapa sistem, tergantung pada pengaturan Anda, hal di atas tidak akan berfungsi, sehingga Anda dapat memahami invers

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

Perhatikan juga, bahwa bit penting adalah -Pflag yang sama dengan --perl-regexp: sehingga akan menginterpretasikan pola Anda sebagai ekspresi reguler Perl. Itu juga mengatakan itu

ini sangat eksperimental dan grep -P dapat memperingatkan fitur yang tidak diimplementasikan.

jerigen
sumber
42
Ini tidak akan berfungsi di BSD grep(pada OS X 10.8 Mountain Lion), karena tidak mendukung Popsi.
Bastiaan M. van de Weerd
20
Untuk memperbarui komentar terakhir saya, versi GNU greptersedia di dupesperpustakaan Homebrew (aktifkan menggunakan brew tap homebrew/dupes):brew install grep
Bastiaan M. van de Weerd
48
@BastiaanVanDeWeerd benar, grep pada OSX 10.8 tidak lagi mendukung PCRE ("ekspresi reguler yang kompatibel dengan Perl") karena Darwin sekarang menggunakan grep BSD bukan grep GNU. Alternatif untuk menginstal dupespustaka adalah menginstal pcre: brew install pcre... sebagai bagian dari ini, Anda akan mendapatkan pcregreputilitas, yang dapat Anda gunakan sebagai berikut:pcregrep --color='auto' -n "[\x80-\xFF]" file.xml
pvandenberk
15
Untuk brewpengguna Mac , coreutils GNU dapat diinstal dengan brew install coreutils. Ini akan memberi Anda banyak alat GNU yang diawali dengan 'g' - dalam hal ini digunakan ggrep. Ini harus menghindari masalah yang timbul dari penggantian utilitas sistem, karena skrip Mac khusus sistem sekarang bergantung pada BSD grep.
Joel Purra
22
ini berfungsi dengan baik di mac ag "[\x80-\xFF]" fileAnda hanya perlu menginstalthe_silver_searcher
slf
123

Daripada membuat asumsi tentang rentang byte karakter non-ASCII, seperti kebanyakan solusi di atas lakukan, itu IMO sedikit lebih baik untuk secara eksplisit tentang kisaran byte sebenarnya dari karakter ASCII sebagai gantinya.

Jadi solusi pertama misalnya akan menjadi:

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

(yang pada dasarnya memahami karakter apa pun di luar rentang ASCII heksadesimal: dari \ x00 hingga \ x7F)

Pada Mountain Lion yang tidak berfungsi (karena kurangnya dukungan PCRE dalam BSD grep) , tetapi dengan pcrediinstal melalui Homebrew, yang berikut ini akan berfungsi juga:

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

Adakah pro atau kontra yang bisa dipikirkan orang?

pvandenberk
sumber
9
Ini sebenarnya bekerja untuk saya ketika solusi di atas gagal. Menemukan tanda kutip $ Word belum semudah ini!
AlbertEngelB
2
Jika Anda memiliki shell yang kompatibel dengan bash tetapi tidak berfungsi pada pcre-grep, LC_COLLATE=C grep $'[^\1-\177]'berfungsi (untuk file tanpa byte nol)
idupree
2
Solusi ini tampaknya bekerja lebih konsisten daripada yang di atas.
0xcaff
1
Saya harus menggunakan ini untuk mengambil Kanji, Cyrillic, dan Mandarin Tradisional di file UTF8 saya, menggunakan "[\ x80- \ xFF]" melewatkan semua ini.
buckaroo1177125
1
Pro adalah ini bekerja dengan baik sementara opsi lain sangat bagus tapi tidak terlalu bagus. Tidak ditemukan kontra sejauh ini.
jwpfox
67

Berikut ini berfungsi untuk saya:

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

Karakter non-ASCII mulai dari 0x80 dan pergi ke 0xFF ketika melihat byte. Grep (dan keluarga) tidak melakukan pemrosesan Unicode untuk menggabungkan karakter multi-byte menjadi satu entitas untuk pencocokan regex seperti yang Anda inginkan. The -Ppilihan dalam grep saya memungkinkan penggunaan \xddlolos di kelas karakter untuk mencapai apa yang Anda inginkan.

Thelema
sumber
1
Untuk tampilan yang mungkin tidak segera tahu bagaimana menyebutnya melalui beberapa file, jalankan: find. -nama * .xml | xargs grep -P "[\ x80- \ xFF]"
David Mohundro
1
Ini memang mengembalikan kecocokan, tetapi tidak ada indikasi karakter apa dan di mana itu. Bagaimana seseorang melihat karakternya, dan di mana itu?
Faheem Mitha
Menambahkan "-n" akan memberikan nomor baris, karakter tambahan yang tidak terlihat akan ditampilkan sebagai blok di terminal: grep -n -P "[\ x80- \ xFF]" file.xml
fooMonster
4
Saya mengalami masalah dengan Hangul Korea: echo '소녀시대' | grep -P "[\x80-\xFF]"tidak mengembalikan apa pun untuk saya - dapatkah orang lain mengkonfirmasi? (GNU grep 2.21)
frabjous
@frabjous Sama di sini, tapi grepping karya-karya terbalik: echo '소녀시대' | grep -P "[^\x00-\x7F]". Atau cukup gunakan the_silver_searcherseperti yang ditunjukkan oleh @slf:echo '소녀시대' | ag "[\x80-\xFF]"
psmith
55

Dalam perl

perl -ane '{ if(m/[[:^ascii:]]/) { print  } }' fileName > newFile
noquery
sumber
1
Pada OSX10.11 saya harus mencoba beberapa solusi grep + regex sebelum menemukan ini yang benar-benar berfungsi
sg
Peduli membagikan solusi OSX ini @sg ?!
geotheory
Skrip perl di atas adalah solusi yang saya bicarakan
sg
5
perl -lne 'print if /[^[:ascii:]]/' file.xml
Naveed
43

Cara mudah adalah mendefinisikan karakter non-ASCII ... sebagai karakter yang bukan karakter ASCII.

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

Tambahkan tab setelah itu ^jika perlu.

Pengaturan LC_COLLATE=Cmenghindari kejutan yang tidak menyenangkan tentang arti rentang karakter di banyak tempat. Pengaturan LC_CTYPE=Cdiperlukan untuk mencocokkan karakter byte tunggal - jika tidak, perintah akan melewatkan urutan byte yang tidak valid dalam pengkodean saat ini. Pengaturan LC_ALL=Cmenghindari efek yang bergantung pada lokal sama sekali.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
Pada RedHat 6.4 dengan tcsh, saya harus menggunakan <<< env LC_COLLATE = C grep -n '[^ - ~]' file.xml >>>. Saya menambahkan -n untuk mendapatkan nomor baris.
ddevienne
Bagi saya echo "A" | LC_COLLATE=C grep '[^ -~]'mengembalikan pertandingan
baik sekali
1
@frabjous Jika Anda miliki LC_ALL=en_US.UTF-8, itu mengalahkan LC_COLLATEpengaturan. Anda seharusnya tidak memiliki ini di lingkungan Anda! LC_ALLhanya untuk memaksa tugas tertentu untuk menggunakan lokal tertentu, biasanya C. Untuk mengatur lokal default untuk semua kategori, atur LANG.
Gilles 'SANGAT berhenti menjadi jahat'
1
Pada awalnya, saya tidak menambahkan LC_ALL=C, berperilaku berbeda di Mac OS X dan Ubuntu. Setelah saya menambahkan pengaturan ini, mereka memberikan hasil yang sama.
Max Peng
1
Ini berfungsi pada Mac, sedangkan solusi berbasis grep lainnya tidak.
Matthias Fripp
26

Berikut adalah varian lain yang saya temukan yang menghasilkan hasil yang sama sekali berbeda dari pencarian grep [\x80-\xFF]dalam jawaban yang diterima. Mungkin akan berguna bagi seseorang untuk menemukan karakter non-ascii tambahan:

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

Catatan: grep (a Mac) komputer saya tidak memiliki -Popsi, jadi saya lakukan brew install grepdan memulai panggilan di atas dengan ggrepalih - alih grep.

ryanm
sumber
2
Sejauh ini, ini adalah jawaban terbaik, karena berfungsi untuk Mac dan juga Linux.
tommy.carstensen
Hanya satu yang bekerja untuk saya di Linux.
9

Kode berikut berfungsi:

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

Ganti /tmpdengan nama direktori yang ingin Anda cari.

pengguna7417071
sumber
2
Pada Mac, ini berfungsi, sementara sebagian besar yang berbasis grep tidak.
Matthias Fripp
9

Mencari karakter yang tidak dapat dicetak. TLDR; Ringkasan bisnis plan

  1. mencari karakter kontrol dan unicode diperpanjang
  2. pengaturan lokal misalnya LC_ALL=Cdiperlukan untuk membuat grep melakukan apa yang Anda harapkan dengan unicode diperpanjang

SO pencari non-ascii char yang disukai:

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

seperti pada jawaban teratas, grep terbalik:

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

seperti pada jawaban teratas tetapi DENGAN LC_ALL=C:

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

. . lebih lanjut. . detail yang luar biasa tentang ini:. . .

Saya setuju dengan Harvey yang terkubur dalam komentar, seringkali lebih berguna untuk mencari karakter yang tidak dapat dicetak ATAU mudah untuk berpikir non-ASCII ketika Anda benar-benar harus berpikir tidak dapat dicetak. Harvey menyarankan "gunakan ini:" [^\n -~]". Tambahkan \ r untuk file teks DOS. Itu berarti" [^\x0A\x020-\x07E]"dan tambahkan \ x0D untuk CR"

Juga, menambahkan -c (tunjukkan jumlah pola yang cocok) ke grep berguna saat mencari karakter yang tidak dapat dicetak karena string yang cocok dapat mengacaukan terminal.

Saya menemukan menambahkan rentang 0-8 dan 0x0e-0x1f (ke kisaran 0x80-0xff) adalah pola yang berguna. Ini tidak termasuk TAB, CR dan LF dan satu atau dua karakter yang tidak biasa dicetak. Jadi IMHO pola grep yang cukup berguna (meskipun kasar) adalah yang INI:

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

SEBENARNYA, secara umum Anda perlu melakukan ini:

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

kerusakan:

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

Contoh praktis penggunaan find untuk menangkap semua file di bawah direktori saat ini:

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

Anda mungkin ingin sesekali menyesuaikan grep. misalnya karakter BS (0x08 - backspace) yang digunakan dalam beberapa file yang dapat dicetak atau untuk mengecualikan VT (0x0B - tab vertikal). Karakter BEL (0x07) dan ESC (0x1B) juga dapat dianggap dapat dicetak dalam beberapa kasus.

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

UPDATE: Saya harus meninjau kembali ini baru-baru ini. Dan, YYMV tergantung pada pengaturan terminal / ramalan cuaca matahari NAMUN. . Saya perhatikan bahwa grep tidak menemukan banyak karakter unicode atau extended. Meskipun secara intuitif mereka harus cocok dengan kisaran 0x80 hingga 0xff, 3 dan 4 byte karakter unicode tidak cocok. ??? Adakah yang bisa menjelaskan ini? IYA. @frabjous bertanya dan @calandoa menjelaskan ituLC_ALL=C harus digunakan untuk mengatur lokal untuk perintah untuk membuat grep cocok.

mis. lokal saya LC_ALL=kosong

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

grep dengan LC_ALL=kecocokan kosong 2 byte karakter yang dikodekan tetapi tidak 3 dan 4 byte yang dikodekan:

$ 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=Ctampaknya cocok dengan semua karakter diperluas yang Anda inginkan:

$ 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

Kecocokan perl INI (sebagian ditemukan di tempat lain di stackoverflow) ATAU grep terbalik pada jawaban atas DO tampaknya menemukan SEMUA karakter ~ aneh ~ dan ~ luar biasa ~ "non-ascii" tanpa menetapkan lokal:

$ 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

SO pencari non-ascii char yang disukai:

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

seperti pada jawaban teratas, grep terbalik:

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

seperti pada jawaban teratas tetapi DENGAN LC_ALL=C:

$ LC_ALL=C grep --color='auto' -P -n "[\x80-\xFF]" notes_unicode_emoji_test
gaoithe
sumber
1
Jawab mengapa grep tidak cocok dengan karakter yang disandikan dalam lebih dari 2 byte, terima kasih kepada @calandoa dan frabjous dalam komentar di atas pada pertanyaan. Gunakan LC_ALL = C sebelum perintah grep.
gaoithe
1
Terima kasih banyak telah repot-repot memposting jawaban yang terkubur di bawah 800 upvotes lainnya! Masalah saya adalah karakter 0x02. Anda mungkin ingin meletakkan "contoh penggunaan praktis" di bagian atas, karena Anda benar-benar tidak perlu membaca seluruh posting hanya untuk melihat apakah itu masalah Anda.
Noumenon
1
Saya tahu, jawaban yang sangat tua, dan detail yang luar biasa, tetapi benar bermanfaat bagi saya dan orang lain juga saya harap. Anda benar, saya menambahkan TLDR; di atas.
gaoithe
1

Anehnya, saya harus melakukan ini hari ini! Saya akhirnya menggunakan Perl karena saya tidak bisa membuat grep / egrep bekerja (bahkan dalam mode -P). Sesuatu seperti:

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

Untuk karakter unicode (seperti \u2212dalam contoh di bawah) gunakan ini:

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 }' '{}' \;
dty
sumber
1

Mungkin menarik untuk mengetahui cara mencari satu karakter unicode. Perintah ini dapat membantu. Anda hanya perlu tahu kode di UTF8

grep -v $'\u200d'
arezae
sumber
Saya bukan benar-benar ahli, tapi saya cukup tahu untuk mengetahui bahwa itu bukan representasi UTF8, itu UTF16, atau mungkin UTF32, atau UCS16. Untuk codepoint 2-byte, ketiganya mungkin sama.
Baxissimo
1

Menemukan semua karakter non-ascii memberi kesan bahwa seseorang sedang mencari string unicode atau bermaksud untuk menghapus karakter tersebut secara individual.

Untuk yang pertama, coba salah satu dari ini (variabel filedigunakan untuk otomatisasi):

 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

Vanilla grep tidak berfungsi dengan benar tanpa LC_ALL = C seperti yang tercantum dalam jawaban sebelumnya.

Kisaran ASCII adalah x00-x7F, ruangx20 , karena string memiliki spasi, rentang negatif menghilangkannya.

Kisaran Non-ASCII adalah x80-xFF , karena string memiliki spasi, rentang positif menambahkannya.

String dianggap setidaknya 7 karakter berturut-turut dalam rentang tersebut. {7,}.

Untuk keluaran yang dapat dibaca shell, uchardet $filemengembalikan tebakan pengkodean file yang diteruskan ke iconv untuk interpolasi otomatis.

noabody
sumber
Ini sangat berguna karena penyebutan uchardetperintah. Terima kasih untuk itu!
bballdave025