ASCII olmayan tüm karakterler için nasıl grep yapabilirim?


360

Birkaç çok büyük XML dosyam var ve ASCII olmayan karakterler içeren satırları bulmaya çalışıyorum. Aşağıdakileri denedim:

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

Ancak bu, satırda belirtilen aralıkta bir karakter içerip içermediğine bakılmaksızın dosyadaki her satırı döndürür.

Sözdizimi yanlış mı yoksa yanlış bir şey mi yapıyorum? Ayrıca denedim:

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

(deseni çevreleyen hem tek hem de çift tırnaklarla).


ASCII karakterleri yalnızca bir bayt uzunluğundadır, bu nedenle dosya unicode değilse 0xFF üzerinde hiçbir karakter olmamalıdır.
zdav

Nasıl \ xFF üstüne çıkacağız? Grep "grep: karakter sınıfında sıra dışı" hatası veriyor.
Mudit Jain

Yanıtlar:


496

Komutu kullanabilirsiniz:

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

Bu size satır numarasını verecektir ve ascii olmayan karakterleri kırmızı ile vurgulayacaktır.

Bazı sistemlerde, ayarlarınıza bağlı olarak yukarıdakiler çalışmaz, böylece tersine grep edebilirsiniz

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

Ayrıca, önemli bitin -Pşuna eşit olan bayrak olduğunu unutmayın --perl-regexp: böylece deseninizi Perl normal ifadesi olarak yorumlayacaktır. Ayrıca şunu da söylüyor:

bu son derece deneyseldir ve grep -P uygulanmamış özellikleri uyarabilir.


42
Bu seçeneği grepdesteklemediğinden BSD'de (OS X 10.8 Mountain Lion'da) çalışmaz P.
Bastiaan M. van de Weerd

20
Son yorumumu güncellemek için, GNU sürümü grepHomebrew'in dupeskütüphanesinde mevcuttur (kullanımı etkinleştir brew tap homebrew/dupes):brew install grep
Bastiaan M. van de Weerd

48
@BastiaanVanDeWeerd doğrudur, OSX 10.8'deki grep artık PCRE'yi ("Perl uyumlu düzenli ifadeler") desteklememektedir, çünkü Darwin artık GNU grep yerine BSD grep kullanmaktadır. dupesKütüphaneyi kurmanın bir alternatifi pcrebunun yerine brew install pcrepcregreppcregrep --color='auto' -n "[\x80-\xFF]" file.xml
kurmaktır

15
Mac brewkullanıcıları için GNU'nun coreutils'leri yüklenebilir brew install coreutils. Bu size bir 'g' ön eki içeren birçok GNU aracı verecektir - bu durumda kullanın ggrep. Sisteme özgü Mac komut dosyaları artık BSD grep'e bağlı olduğundan, bu durum bir sistem yardımcı programının değiştirilmesinden kaynaklanan sorunlardan kaçınmalıdır.
Joel Purra

22
Bu ag "[\x80-\xFF]" filesadece yüklemeniz gereken bir mac üzerinde iyi çalışıyorthe_silver_searcher
slf

123

Yukarıdaki çözümlerin çoğunun yaptığı gibi ASCII olmayan karakterlerin bayt aralığı hakkında varsayımlar yapmak yerine, bunun yerine ASCII karakterlerinin gerçek bayt aralığı hakkında açık olmak biraz daha iyi IMO'dur.

Böylece ilk çözüm şöyle olur:

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

(temelde onaltılı ASCII aralığının dışındaki herhangi bir karakter için açılır: \ x00 ila \ x7F)

Çalışmayan Mountain Lion'da (BSD grep'te PCRE desteğinin olmaması nedeniyle) , ancak pcreHomebrew aracılığıyla yüklendiğinde aşağıdakiler de işe yarayacaktır:

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

Herkesin düşünebileceği herhangi bir artı veya eksileri?


9
Bu aslında yukarıdaki çözümlerin başarısız olduğu yerlerde benim için çalıştı. M $ Word kesme işaretlerini bulmak hiç bu kadar kolay olmamıştı!
AlbertEngelB

2
Eğer bash uyumlu bir kabuğunuz varsa, ancak pcre-grep çalışmıyorsa, LC_COLLATE=C grep $'[^\1-\177]'çalışır (boş baytları olmayan dosyalar için)
idupree

2
Bu çözüm, yukarıdaki çözümlerden daha tutarlı çalışıyor gibi görünüyor.
0xcaff

1
UTF8 dosyamda Kanji, Kiril ve Geleneksel Çince'yi almak için bunu kullanmak zorunda kaldım, "[\ x80- \ xFF]" kullanarak bunların hepsini kaçırdım.
buckaroo1177125

1
Pro diğer seçenekleri büyük ama o kadar büyük değil bu mükemmel çalıştı. Şimdiye kadar eksileri bulunamadı.
jwpfox

67

Aşağıdakiler benim için çalışıyor:

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

ASCII olmayan karakterler 0x80'de başlar ve baytlara bakarken 0xFF'e gider. Grep (ve ailesi), istediğiniz gibi normal ifade eşleşmesi için çok baytlık karakterleri tek bir varlıkta birleştirmek için Unicode işlemesi yapmaz. -PBenim grep seçeneğini kullanımına izin \xddistediğini başarmak için karakter sınıflarında kaçması.


1
Bunu birden çok dosya üzerinden nasıl arayacağınızı hemen bilmeyebilecek görünüm için şunu çalıştırın: find. -adi * .xml | xargs grep -P "[\ x80- \ xFF]"
David Mohundro

1
Bu bir eşleşme döndürür, ancak karakterin ne olduğu ve nerede olduğuna dair bir gösterge yoktur. Kişinin karakterin ne olduğunu ve nerede olduğunu nasıl görür?
Faheem Mitha

"-N" eklenmesi satır numarasını verir, ayrıca görünmeyen karakterler terminalde bir blok olarak gösterilir: grep -n -P "[\ x80- \ xFF]" file.xml
fooMonster

4
Hangul Korece ile ilgili bir sorunum var: echo '소녀시대' | grep -P "[\x80-\xFF]"benim için hiçbir şey döndürmüyor - başka biri onaylayabilir mi? (GNU grep 2.21)
15'te

@frabjous Aynen, ama ters işler grepping: echo '소녀시대' | grep -P "[^\x00-\x7F]". Veya sadece the_silver_searcher@slf tarafından işaret edildiği gibi kullanın:echo '소녀시대' | ag "[\x80-\xFF]"
psmith

55

Perl cinsinden

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

1
OSX10.11'de gerçekten işe yarayan bulmadan önce birkaç grep + regex çözümünü denemek zorunda kaldım
sg

Bu OSX çözümünü paylaşmak ister misiniz?
geotheory

Yukarıdaki perl betiği, bahsettiğim çözüm
sg

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

43

Kolay yol, ASCII olmayan bir karakteri ... ASCII karakteri olmayan bir karakter olarak tanımlamaktır.

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

^Gerekirse bir sekme ekleyin .

Ortam ayarı LC_COLLATE=C, birçok yerde karakter aralıklarının anlamı konusunda kötü sürprizlerden kaçınır. LC_CTYPE=CTek baytlık karakterleri eşleştirmek için ayar gereklidir - aksi takdirde komut geçerli kodlamadaki geçersiz bayt dizilerini kaçıracaktır. Ayarlama LC_ALL=C, yerel ayara bağlı etkileri tamamen önler.


Tcsh ile RedHat 6.4 üzerinde, <<< env LC_COLLATE = C grep -n '[^ - ~]' file.xml >>> kullanmak zorunda kaldım. Satır numarasını almak için -n ekledim.
ddevienne

Benim için echo "A" | LC_COLLATE=C grep '[^ -~]'bir maç döndürüyor
Ocak'ta

1
@frabjous Varsa LC_ALL=en_US.UTF-8, bu LC_COLLATEayarın üstesinden gelir . Bunu ortamınızda bulundurmamalısınız! LC_ALLyalnızca belirli bir görevi genellikle belirli bir yerel ayarı kullanmaya zorlamaktır C. Tüm kategoriler için varsayılan yerel ayarı ayarlamak için seçeneğini belirleyin LANG.
Gilles 'SO- kötü olmayı bırak'

1
İlk başta, eklemedim LC_ALL=C, Mac OS X ve Ubuntu'da farklı davranıyor. Bu ayarı ekledikten sonra aynı sonucu verir.
Max Peng

1
Bu Mac'te çalışır, diğer grep tabanlı çözümler işe yaramaz.
Matthias Fripp

26

Kabul edilen cevapta grep aramasından tamamen farklı sonuçlar üreten bir başka varyant da var [\x80-\xFF]. Belki de ascii olmayan ek karakterler bulmak birileri için yararlı olacaktır:

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

Not: Bilgisayarımın grep (Mac) -Pseçeneği yoktu, bu yüzden brew install grepyukarıdaki çağrıyı ggrepbunun yerine ile başladım ve başlattım grep.


2
Bu, Mac'in yanı sıra Linux için de çalıştığı için en iyi cevaptır.
tommy.carstensen

9

Aşağıdaki kod çalışır:

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

/tmpAramak istediğiniz dizinin adıyla değiştirin .


2
Mac'te bu işe yarar, grep tabanlı olanların çoğu çalışmıyor.
Matthias Fripp

9

Yazdırılamayan karakterleri arama. TLDR; Yönetici Özeti

  1. kontrol karakterleri ve genişletilmiş unicode arayın
  2. yerel ayar, örneğin LC_ALL=Cgrep'in genişletilmiş unicode ile beklediğiniz şeyi yapmasını sağlamak için gereklidir

SO tercih edilen ascii olmayan char bulucular:

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

üst cevapta olduğu gibi, ters grep:

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

üst yanıtta olduğu gibi ama İLE LC_ALL=C:

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

. . Daha . . bu konuda dayanılmaz detay:. . .

Yukarıdaki yorumlarda gömülü olan Harvey ile aynı fikirdeyim, yazdırılamayan karakterleri aramak genellikle daha yararlıdır VEYA gerçekten yazdırılamayacak şekilde düşünmeniz gerektiğinde ASCII olmayan düşünmek kolaydır. Harvey "şunu kullanın:" [^\n -~]" önerir . DOS metin dosyaları için \ r ekleyin." [^\x0A\x020-\x07E]"Anlamına gelir ve CR için \ x0D ekleyin"

Ayrıca, eşleşen dizeler terminali bozabileceğinden, yazdırılamayan karakterleri ararken grep'e -c (eşleşen desen sayısını göster) eklemek yararlıdır.

0-8 ve 0x0e-0x1f aralığını (0x80-0xff aralığına) eklemenin yararlı bir model olduğunu buldum. Bu, SEKME, CR ve LF ve bir veya iki daha nadir yazdırılabilir karakter içermez. Yani IMHO oldukça yararlı (ham de olsa) bir grep modeli BU:

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

GERÇEKTEN, genellikle bunu yapmanız gerekir:

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

Yıkmak:

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

Örneğin pratik kullanım örneği find, mevcut dizin altındaki tüm dosyaları grep için:

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

Grep'i zaman zaman ayarlamak isteyebilirsiniz. örneğin bazı yazdırılabilir dosyalarda veya VT'yi (0x0B - dikey sekme) hariç tutmak için kullanılan BS (0x08 - geri silme) karakterleri. BEL (0x07) ve ESC (0x1B) karakterleri de bazı durumlarda yazdırılabilir olarak kabul edilebilir.

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

GÜNCELLEME: Bunu son zamanlarda tekrar ziyaret etmek zorunda kaldım. Ve, YYMV terminal ayarlarına / güneş hava tahminlerine bağlı olarak AMA. . Grep'in birçok unicode veya genişletilmiş karakter bulamadığını fark ettim . Sezgisel olarak 0x80 ila 0xff aralığına uymaları gerekse de, 3 ve 4 baytlık unicode karakterler eşleşmedi. ??? Herkes bunu açıklayabilir mi? EVET. @ frabjous sordu ve @calandoa, LC_ALL=Cgrep eşleşmesi yapma komutunun yerel ayarını yapmak için kullanılması gerektiğini açıkladı .

örneğin yerel ayarım LC_ALL=boş

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

LC_ALL=boş eşlemeler 2 bayt kodlanmış karakterlerle grep, ancak 3 ve 4 bayt kodlanmış değil:

$ 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=Cistediğiniz tüm genişletilmiş karakterlerle eşleşiyor gibi görünüyor:

$ 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

BU perl eşleşmesi (kısmen stackoverflow üzerinde başka bir yerde bulunur) VEYA üst cevaptaki ters grep, yerel ayarı ayarlamadan TÜM ~ garip ~ ve ~ harika ~ "ascii olmayan" karakterleri buluyor gibi görünüyor:

$ 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 tercih edilen ascii olmayan char bulucular:

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

üst cevapta olduğu gibi, ters grep:

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

üst yanıtta olduğu gibi ama İLE LC_ALL=C:

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

1
Yukarıdaki yorumlarda @calandoa ve frabjous sayesinde grep'in neden 2 bayttan fazla kodlanmış karakterlerle eşleşmediğinin cevabı. Grep komutundan önce LC_ALL = C kullanın.
gaoithe

1
800 diğer upvotes altında gömülü bir cevap göndermek için rahatsız ettiğiniz için çok teşekkürler! Benim sorunum bir 0x02 karakterdi. Bu "pratik kullanım örneğini" en üste koymak isteyebilirsiniz, çünkü sadece sorununun bu olup olmadığını görmek için tüm gönderiyi okumak zorunda değilsiniz.
Noumenon

1
Biliyorum, gerçekten eski cevap ve heyecan verici detay, ama doğru benim ve diğerleri için yararlı umarım. Haklısın, TLDR ekledim; zirvede.
gaoithe

1

Garip bir şekilde, bunu bugün yapmak zorundaydım! Perl kullanarak sona erdi çünkü grep / egrep'i (-P modunda bile) çalıştıramadım. Gibi bir şey:

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

Unicode karakterler için ( \u2212aşağıdaki örnekte olduğu gibi) bunu kullanın:

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

Bir unicode karakterin nasıl aranacağını bilmek ilginç olabilir. Bu komut yardımcı olabilir. Sadece UTF8'deki kodu bilmeniz gerekir

grep -v $'\u200d'

Gerçekten uzman değilim, ama bunun UTF8 temsili olmadığını, UTF16 veya belki UTF32 veya UCS16 olduğunu bilmek için yeterli biliyorum. 2 baytlık bir kod noktası için bu üçünün hepsi aynı olabilir.
Baxissimo

1

Ascii olmayan tüm karakterleri bulmak, birisinin unicode dizeleri aradığı veya adı geçen karakterleri ayrı ayrı soyma niyetinde olduğu izlenimini verir.

Birincisi için bunlardan birini deneyin (değişken fileotomasyon için kullanılır):

 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, önceki cevaplarda belirtildiği gibi LC_ALL = C olmadan düzgün çalışmıyor.

ASCII aralığı x00-x7F, boşluk, x20dizeler boşluklara sahip olduğu için negatif aralık onu atlar.

ASCII olmayan aralık, x80-xFFdizelerde boşluk bulunduğundan, pozitif aralık onu ekler.

Dizenin aralık içinde en az 7 ardışık karakter olduğu varsayılır. {7,}.

Kabuk okunabilir çıktısı uchardet $fileiçin, otomatik enterpolasyon için iconv'ye aktarılan dosya kodlamasının bir tahminini döndürür.


Bu, uchardetkomuttan bahsedildiği için çok kullanışlıdır . Bu haber için teşekkürler!
bballdave025
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.