Mam plik tekstowy w nieznanym lub mieszanym kodowaniu. Chcę zobaczyć wiersze zawierające sekwencję bajtów, która jest niepoprawna UTF-8 (przez przesłanie pliku tekstowego do jakiegoś programu). Odpowiednio chcę odfiltrować wiersze, które są poprawne UTF-8. Innymi słowy, szukam .grep [notutf8]
Idealne rozwiązanie byłoby przenośne, krótkie i możliwe do uogólnienia w stosunku do innych kodowań, ale jeśli uważasz, że najlepszym sposobem jest upieczenie w definicji UTF-8 , śmiało.
command-line
text-processing
character-encoding
unicode
Gilles „SO- przestań być zły”
źródło
źródło
Odpowiedzi:
Jeśli chcesz użyć
grep
, możesz:w ustawieniach regionalnych UTF-8, aby uzyskać linie, które mają co najmniej nieprawidłową sekwencję UTF-8 (działa to przynajmniej z GNU Grep).
źródło
-a
tym jest to wymagane do pracy przez POSIX. Jednak GNUgrep
przynajmniej nie dostrzega zakodowanych w znakach UTF-8 znaków zastępczych UTF-16 lub znaków kodowych powyżej 0x10FFFF.-a
jest to potrzebne GNUgrep
(który, jak zakładam, nie jest zgodny z POSIX). Jeśli chodzi o obszar zastępczym i codepoints Powyższe 0x10FFFF, jest to bug to (co może tłumaczyć , że ). W tym celu dodawanie-P
powinno działać z GNUgrep
2.21 (ale jest powolne); jest wadliwy przynajmniej w Debianie grep / 2.20-4 .grep
jest narzędziem tekstowym (oczekuje się, że będzie działać tylko przy wprowadzaniu tekstu), więc przypuszczam, że zachowanie GNU grep jest tak samo ważne jak tutaj.grep
(której celem jest uznanie nieprawidłowych sekwencji za niepasujące) i możliwych błędów.Myślę, że prawdopodobnie chcesz iconv . Służy do konwersji między zestawami kodów i obsługuje absurdalną liczbę formatów. Na przykład, aby usunąć wszystko, co nie jest poprawne w UTF-8, możesz użyć:
iconv -c -t UTF-8 < input.txt > output.txt
Bez opcji -c zgłasza problemy z konwersją do stderr, więc z kierunkiem procesu możesz zapisać ich listę. Innym sposobem byłoby usunięcie elementów innych niż UTF8, a następnie
diff input.txt output.txt
po listę, gdzie dokonano zmian.
źródło
iconv -c -t UTF-8 <input.txt | diff input.txt - | sed -ne 's/^< //p'
. Nie będzie to jednak działało jako potok, ponieważ musisz odczytać dane wejściowe dwa razy (nie,tee
nie zrobi się, może blokować w zależności od tego, ile buforowaniaiconv
idiff
zrobić).diff <(iconv -c -t UTF-8 <input.txt) input.txt
Edycja: Naprawiłem błąd literowy w wyrażeniu regularnym. Potrzebowałem „\ x80”, a nie \ 80 .
Wyrażenie regularne, aby odfiltrować nieprawidłowe formularze UTF-8, w celu ścisłego przestrzegania UTF-8, jest następujące
Wyjście (kluczowych linii. Z testu 1 ):
P: Jak można utworzyć dane testowe w celu przetestowania wyrażenia regularnego, które filtruje nieprawidłowy kod Unicode?
A. Stwórz swój własny algorytm testowy UTF-8 i złam jego zasady ...
Catch-22 .. Ale w jaki sposób następnie testujesz swój algorytm testowy?
Wyrażenie regularne powyżej zostało przetestowane (przy użyciu
iconv
jako odniesienie) dla każdej wartości całkowitej od0x00000
do0x10FFFF
.. Ta górna wartość jest maksymalną wartością całkowitą Unicode CodepointWedług tej strony wikipedii UTF-8 .
Ten numeber (1112064) równa się w zakresie
0x000000
do0x10F7FF
, co 0x0800 trwożliwa rzeczywistej maksymalnej całkowitej wartości dla najwyższej kodowy Unikodu:0x10FFFF
Brakuje tego bloku liczb całkowitych w widmie Unicode Codepoints, ponieważ kodowanie UTF-16 musi wykraczać poza pierwotne założenia projektowe za pośrednictwem systemu zwanego parami zastępczymi . Blok
0x0800
liczb całkowitych został zarezerwowany do użycia przez UTF-16 .. Blok ten obejmuje zakres0x00D800
do0x00DFFF
. Żaden z tych parametrów nie jest prawidłowymi wartościami Unicode, a zatem są nieprawidłowymi wartościami UTF-8.W teście 1 ,
regex
został przetestowany przed każdym numerem w zakresie codepoints Unicode i pasuje exectly wynikiiconv
.. tzn. 0x010F7FF prawidłowe wartości i 0x000800 nieprawidłowe wartości.Jednak teraz pojawia się problem: * Jak wyrażenie regularne obsługuje wartość UTF-8 poza zakresem; powyżej
0x010FFFF
(UTF-8 może rozciągać się do 6 bajtów, przy maksymalnej wartości całkowitej 0x7FFFFFFF ?Aby wygenerować niezbędne * wartości bajtów UTF-8 inne niż Unicode , użyłem następującej komendy:
Aby przetestować ich ważność (w pewien sposób), użyłem
Gilles'
wyrażenia regularnego UTF-8 ...Wyjście „perl's print chr” pasuje do filtrowania wyrażenia regularnego Gillesa. Jeden wzmacnia ważność drugiego .. Nie mogę użyć,
iconv
ponieważ obsługuje tylko prawidłowy podzbiór Standard Unicode szerszego (oryginalnego) UTF-8 standard...Zaangażowane zakonnice są raczej duże, więc przetestowałem top-of-range, bottom-of-range i kilka skanów krok po kroku, takich jak 11111, 13579, 33333, 53441 ... Wszystkie wyniki są zgodne, więc teraz wszystko, co pozostaje, to przetestowanie wyrażenia regularnego względem tych wartości poza zakresem w stylu UTF-8 (niepoprawne dla Unicode, a zatem również niepoprawne dla samego ścisłego UTF-8).
Oto moduły testowe:
źródło
\300\200
(naprawdę źle: to kod 0 nie jest wyrażony bajtem zerowym!). Myślę, że wyrażenie regularne odrzuca je poprawnie.Uważam
uconv
(wicu-devtools
pakiecie w Debianie) za przydatne do sprawdzania danych UTF-8:(
\x
Pomoc w wykrywaniu nieprawidłowych znaków (z wyjątkiem fałszywie dodatnich dobrowolnie wprowadzonych literałem\xE9
powyżej)).(wiele innych miłych zastosowań).
źródło
recode
można go używać podobnie - z tym wyjątkiem, że powinien się nie powieść, jeśli zostanie poproszony o przetłumaczenie nieprawidłowej sekwencji wielobajtowej. Nie jestem jednak pewien; nie zawiedzie naprint...|recode u8..u8/x4
przykład (co właśnie robi hexdump jak ty powyżej) , ponieważ nic nie robi, aleiconv data data
, ale nie zawieść jakrecode u8..u2..u8/x4
, ponieważ przekłada się następnie drukuje. Ale nie wiem o tym wystarczająco dużo - i jest wiele możliwości.test.txt
. Jak powinienem znaleźć nieprawidłowy znak za pomocą twojego rozwiązania? Co oznaczaus
twój kod?us
oznacza Stany Zjednoczone, czyli skrót ASCII. Konwertuje dane wejściowe na ASCII, w których znaki spoza ASCII są konwertowane na\uXXXX
notację, a znaki niebędące znakami na\xXX
.Python ma wbudowaną
unicode
funkcję od wersji 2.0.W Pythonie 3
unicode
został złożonystr
. Należy przekazać obiekt podobny do bajtu , tutaj podstawowebuffer
obiekty dla standardowych deskryptorów .źródło
python 2
nie oznacza oflagowania znaków niebędących znakami UTF-16 zakodowanych w UTF-8 (przynajmniej w wersji 2.7.6).Natknąłem się na podobny problem (szczegółowo w sekcji „Kontekst”) i przybyłem z następującym rozwiązaniem ftfy_line_by_line.py :
Używanie kodowania + zamień + ftfy, aby automatycznie naprawić Mojibake i inne poprawki.
Kontekst
Zebrałem> 10GiB CSV podstawowych metadanych systemu plików, używając następującego skryptu gen_basic_files_metadata.csv.sh , działającego zasadniczo:
Problemem było z niezgodnego kodowania nazwami całym systemie plików, powodując
UnicodeDecodeError
podczas dalszego przetwarzania w aplikacjach pytona ( csvsql się bardziej specyficzne).Dlatego zastosowałem powyżej skryptu ftfy i trzeba było
Pamiętaj, że ftfy działa dość wolno, przetwarzanie tych> 10GiB zajęło:
podczas gdy sha256sum dla porównania:
na procesorze Intel (R) Core i7-3520M @ 2.90GHz + 16GiB RAM (i dane na dysku zewnętrznym)
źródło