grep
je pro danou úlohu nesprávným nástrojem.
Zobrazí se � U+FFFD REPLACEMENT CHARACTER
ne proto, že je to doslova v obsahu souboru, ale protože jste se podívali na binární soubor pomocí nástroje, který má zpracovávat pouze textový vstup. Standardní způsob, jak zpracovat neplatný vstup (tj. náhodná binární data), je nahradit vše, co není platné v aktuálním národním prostředí (pravděpodobně UTF-8), U+FFFD, než se objeví na obrazovce.
To znamená, že je velmi pravděpodobné, že doslovný \xEF\xBF\xBD
(sekvence bajtů UTF-8 pro znak U+FFFD) se v souboru nikdy nevyskytuje. grep
má úplnou pravdu, když vám říkám, že žádný neexistuje.
Jedním ze způsobů, jak zjistit, zda soubor obsahuje nějakou neznámou binární hodnotu, je file(1)
příkaz:
$ head -c 100 /dev/urandom > rubbish.bin
$ file rubbish.bin
rubbish.bin: data
Pro jakýkoli neznámý typ souboru to jednoduše řekne data
. Zkuste
$ file out.txt | grep '^out.txt: data$'
zkontrolovat, zda soubor skutečně obsahuje libovolný binární soubor a tedy s největší pravděpodobností odpad.
Pokud se chcete ujistit, že out.txt
je pouze textový soubor s kódováním UTF-8, můžete alternativně použít iconv
:
$ iconv -f utf-8 -t utf-16 out.txt >/dev/null
TL;DR:
grep -axv '.*' out.txt
dlouhá odpověď
Obě současné odpovědi jsou extrémně zavádějící a v zásadě špatné.
Chcete-li otestovat, získejte tyto dva soubory (od velmi uznávaného vývojáře:Markuse Kuhna):
$ wget https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-demo.txt
$ wget https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
Ukázka
První UTF-8-demo.txt
je soubor navržený tak, aby ukázal, jak dobře dokáže UTF-8 prezentovat mnoho jazyků, matematiku, Braillovo písmo a mnoho dalších užitečných typů znaků. Podívejte se pomocí textového editoru (který rozumí utf-8) a uvidíte spoustu příkladů a ne �
.
Test, který navrhuje jedna odpověď:omezit rozsah znaků na \x00-\x7F
odmítne téměř vše v tomto souboru.
To je velmi špatné a neodstraní žádné �
protože v tom souboru žádný není .
Pomocí testu doporučeného v této odpovědi odstraníte 72.5 %
souboru:
$ grep -oP "[^\x00-\x7F]" UTF-8-demo.txt | tr -d '\n' | wc -c
10192
$ cat UTF-8-demo.txt | wc -c
14058
To je (pro většinu praktických účelů) celý soubor. Soubor velmi dobře navržený tak, aby zobrazoval dokonale platné znaky.
Test
Druhý soubor je navržen tak, aby vyzkoušel několik hraničních případů, aby se potvrdilo, že čtečky utf-8 odvádějí dobrou práci. Obsahuje mnoho znaků, které způsobí zobrazení '�'. Ale doporučení druhé odpovědi (vybrané) použít file
s tímto souborem hrubě selže. Pouze odstranění nulového bajtu (\0
) (což je technicky platné ASCII) a \x7f
byte (DEL - delete) (což je jasně také znak ASCII) vytvoří vše soubor platný pro file
příkaz:
$ cat UTF-8-test.txt | tr -d '\0\177' > a.txt
$ file a.txt
a.txt: Non-ISO extended-ASCII text, with LF, NEL line terminators
Nejen file
nepodařilo zjistit mnoho nesprávné znaky, ale také nedokáže detekovat a nahlásit, že se jedná o soubor s kódováním UTF-8.
A ano, file
je schopen detekovat a hlásit text kódovaný UTF-8:
$ echo "ééakjfhhjhfakjfhfhaéá" | file -
/dev/stdin: UTF-8 Unicode text
Také file
nehlásí jako ASCII většinu řídicích znaků v rozsahu 1 až 31. It (file
) uvádí některé rozsahy jako data
:
$ printf '%b' "$(printf '\\U%x' {1..6})" | file -
/dev/stdin: data
Ostatní jako ASCII text
:
$ printf '%b' "$(printf '\\U%x' 7 {9..12})" | file -
/dev/stdin: ASCII text
Jako rozsah tisknutelných znaků (s novými řádky):
$ printf '%b' "$(printf '\\U%x' {32..126} 10)" | file -
/dev/stdin: ASCII text
Některé rozsahy však mohou způsobit podivné výsledky:
$ printf '%b' "$(printf '\\U%x' {14..26})" | file -
/dev/stdin: Atari MSA archive data, 4113 sectors per track, starting track: 5141, ending track: 5655
Program file
není nástroj k detekci textu, ale k detekci kouzla čísla ve spustitelných programech nebo souborech.
Rozsahy file
detekovat a odpovídající typ, který jsem našel, byl:
-
Jednobajtové hodnoty, většinou ascii:
{1..6} {14..26} {28..31} 127 :data {128..132} {134..159} :Non-ISO extended-ASCII text 133 :ASCII text, with LF, NEL line terminators 27 :ASCII text, with escape sequences 13 :ASCII text, with CR, LF line terminators 8 :ASCII text, with overstriking 7 {9..12} {32..126} :ASCII text {160..255} :ISO-8859 text
-
Rozsahy kódované Utf-8:
{1..6} {14..26} {28..31} 127 :data 27 :ASCII text, with escape sequences 13 :ASCII text, with CR, LF line terminators 8 :ASCII text, with overstriking 7 {9..12} {32..126} :ASCII text {128..132} {134..159} :UTF-8 Unicode text 133 :UTF-8 Unicode text, with LF, NEL line terminators {160..255} :UTF-8 Unicode text {256..5120} :UTF-8 Unicode text
Jedno z možných řešení je uvedeno níže.
Předchozí odpověď.
Hodnota Unicode pro znak, který posíláte, je:
$ printf '%x\n' "'�"
fffd
Ano, to je znak Unicode 'NÁHRADNÍ ZNAK' (U+FFFD). To je znak používaný k nahrazení jakéhokoli neplatného V textu byl nalezen znak Unicode. Je to „vizuální pomůcka“, nikoli skutečná postava. Chcete-li najít a vypsat každý celý řádek, který obsahuje neplatný kód UNICODE znaky používají:
grep -axv '.*' out.txt
ale pokud chcete pouze zjistit, zda je některý znak neplatný, použijte:
grep -qaxv '.*' out.txt; echo $?
Pokud je výsledek 1
soubor je čistý, jinak bude nula 0
.
Pokud jste se ptali:jak najít �
znak, pak použijte toto:
➤ a='Basically, if the file "out.txt" contains "�" anywhere in the file I'
➤ echo "$a" | grep -oP $(printf %b \\Ufffd)
�
Nebo pokud váš systém správně zpracovává text UTF-8, jednoduše:
➤ echo "$a" | grep -oP '�'
�
Tato velmi časná odpověď byla pro původní příspěvek, který byl:
Jak grep pro unicode � ve skriptu bash
if grep -q "�" out.txt then echo "working" else cat out.txt fi
V zásadě, pokud soubor „out.txt“ obsahuje „�“ kdekoli v souboru, chtěl bych, aby se opakoval „working“ A pokud soubor „out.txt“ nikde v souboru NEobsahuje „�“, pak bych to chtěl to cat out.txt
Zkuste
grep -oP "[^\x00-\x7F]"
s if .. then
prohlášení takto:
if grep -oP "[^\x00-\x7F]" file.txt; then
echo "grep found something ..."
else
echo "Nothing found!"
fi
Vysvětlení:
-P
,--perl-regexp
:PATTERN je regulární výraz jazyka Perl-o
,--only-matching
:zobrazí pouze část řádku odpovídající PATTERN[^\x00-\x7F]
je regulární výraz, který odpovídá jedinému znaku mimo ASCII.[[:ascii:]]
- odpovídá jednomu ASCII znaku[^[:ascii:]]
- odpovídá jednomu ne-ASCII znaku
v bash
LC_COLLATE=C grep -o '[^ -~]' file