W pracy wydaje się, że żaden tydzień nie mija bez związanej z kodowaniem żałoby, nieszczęścia lub katastrofy. Problem zwykle pochodzi od programistów, którzy uważają, że mogą niezawodnie przetworzyć plik „tekstowy” bez określania kodowania. Ale nie możesz.
Postanowiono więc odtąd zabronić plikom kiedykolwiek nazw kończących się na *.txt
lub *.text
. Uważa się, że te rozszerzenia wprowadzają zwykłego programistę w tępe zadowolenie z kodowania, a to prowadzi do niewłaściwej obsługi. Prawie lepiej byłoby nie mieć żadnego rozszerzenia, ponieważ przynajmniej wtedy wiesz , że nie wiesz, co masz.
Jednak nie posuniemy się tak daleko. Zamiast tego oczekuje się, że użyjesz nazwy pliku, która kończy się kodowaniem. Więc dla plików tekstowych, na przykład, to byłoby coś README.ascii
, README.latin1
, README.utf8
, itd.
W przypadku plików wymagających określonego rozszerzenia, jeśli można określić kodowanie w samym pliku, na przykład w Perlu lub Pythonie, należy to zrobić. W przypadku plików takich jak źródło Java, w przypadku których nie istnieje taka funkcja wewnątrz pliku, kodowanie należy umieścić przed rozszerzeniem, takim jak SomeClass-utf8.java
.
W przypadku wyjścia zdecydowanie preferowany jest UTF-8 .
Ale aby wprowadzić dane, musimy dowiedzieć się, jak radzić sobie z tysiącami plików w naszej bazie kodu o nazwie *.txt
. Chcemy zmienić ich nazwy, aby pasowały do naszego nowego standardu. Ale nie możemy ich wszystkich przyjrzeć. Potrzebujemy więc biblioteki lub programu, który faktycznie działa.
Są różne w ASCII, ISO-8859-1, UTF-8, Microsoft CP1252 lub Apple MacRoman. Chociaż wiemy, że możemy stwierdzić, czy coś jest ASCII, i znosimy dobrą zmianę, wiedząc, czy coś jest prawdopodobnie UTF-8, jesteśmy zaskoczeni kodowaniem 8-bitowym. Ponieważ pracujemy w mieszanym środowisku Unix (Solaris, Linux, Darwin), gdzie większość komputerów stacjonarnych to komputery Mac, mamy sporo irytujących plików MacRoman. A to szczególnie stanowi problem.
Od jakiegoś czasu szukałem sposobu, aby programowo określić, który z nich
- ASCII
- ISO-8859-1
- CP1252
- MacRoman
- UTF-8
plik się znajduje, a nie znalazłem programu ani biblioteki, która mogłaby niezawodnie rozróżnić te trzy różne 8-bitowe kodowanie. Prawdopodobnie mamy ponad tysiąc samych plików MacRoman, więc niezależnie od używanego przez nas detektora znaków, musi być w stanie je wykryć. Nic, na co patrzyłem, nie poradzi sobie z tą sztuczką. Miałem duże nadzieje co do biblioteki do wykrywania zestawów znaków ICU , ale nie radzi sobie ona z MacRomanem. Przyjrzałem się także modułom, które wykonują to samo w Perlu i Pythonie, ale zawsze jest to ta sama historia: brak obsługi wykrywania MacRoman.
Dlatego szukam istniejącej biblioteki lub programu, który niezawodnie określa, w którym z tych pięciu kodowań znajduje się plik - a najlepiej więcej. W szczególności musi rozróżniać trzy 3-bitowe kodowanie, które zacytowałem, zwłaszcza MacRoman . Pliki zawierają ponad 99% tekstu w języku angielskim; jest kilka w innych językach, ale niewiele.
Jeśli jest to kod biblioteki, preferujemy język Perl, C, Java lub Python i w tej kolejności. Jeśli jest to tylko program, to nie obchodzi nas, w jakim języku jest, o ile jest w pełnym kodzie źródłowym, działa w systemie Unix i jest całkowicie wolny od obciążeń.
Czy ktoś inny miał ten problem z milionami starszych plików tekstowych, które zostały losowo zakodowane? Jeśli tak, w jaki sposób próbowałeś go rozwiązać i jaki był sukces? To jest najważniejszy aspekt mojego pytania, ale interesuje mnie również, czy myślisz, że zachęcanie programistów do nadawania nazw (lub zmiany nazwy) swoich plików za pomocą faktycznego kodowania tych plików pomoże nam uniknąć problemu w przyszłości. Czy ktokolwiek próbował wyegzekwować to na zasadach instytucjonalnych, a jeśli tak, to czy to się udało, czy nie, i dlaczego?
I tak, w pełni rozumiem, dlaczego nie można zagwarantować jednoznacznej odpowiedzi, biorąc pod uwagę naturę problemu. Dotyczy to zwłaszcza małych plików, w przypadku których nie masz wystarczającej ilości danych, aby kontynuować. Na szczęście nasze pliki rzadko są małe. Oprócz README
plików losowych większość ma rozmiar od 50 KB do 250 KB, a wiele z nich jest większych. Wszystko, co przekracza kilka K, na pewno będzie w języku angielskim.
Domeną problemu jest eksploracja tekstu biomedycznego, więc czasami mamy do czynienia z rozległymi i bardzo dużymi korpusami, takimi jak wszystkie repozytorium Open Access PubMedCentral. Dość dużym plikiem jest BioThesaurus 6.0 o rozmiarze 5,7 gigabajta. Ten plik jest szczególnie denerwujący, ponieważ prawie cały jest UTF-8. Jednak jakiś numbskull poszedł i umieścił w nim kilka wierszy, które są w 8-bitowym kodowaniu - chyba Microsoft CP1252. Trwa trochę czasu, zanim się na niego potkniesz. :(
Odpowiedzi:
Po pierwsze, proste przypadki:
ASCII
Jeśli twoje dane nie zawierają bajtów powyżej 0x7F, to jest to ASCII. (Lub 7-bitowe kodowanie ISO646, ale są one bardzo przestarzałe).
UTF-8
Jeśli Twoje dane sprawdzają się jako UTF-8, możesz bezpiecznie założyć, że jest to UTF-8. Ze względu na ścisłe zasady walidacji UTF-8 fałszywe alarmy są niezwykle rzadkie.
ISO-8859-1 a Windows-1252
Jedyną różnicą między tymi dwoma kodowaniami jest to, że ISO-8859-1 zawiera znaki sterujące C1, a windows-1252 - znaki drukowalne € ‚ƒ„… † ‡ ˆ ‰ Š ‹ŒŽ ''„ ”• –—˜ ™ š› œžŸ. Widziałem wiele plików, które używają cudzysłowów lub myślników, ale żaden nie używa znaków sterujących C1. Więc nawet nie przejmuj się nimi lub ISO-8859-1, po prostu wykryj zamiast tego windows-1252.
To teraz pozostawia tylko jedno pytanie.
Jak odróżniasz MacRoman od cp1252?
To jest o wiele trudniejsze.
Niezdefiniowane znaki
Bajty 0x81, 0x8D, 0x8F, 0x90, 0x9D nie są używane w systemie Windows 1252. Jeśli wystąpią, załóżmy, że dane to MacRoman.
Identyczne postacie
Bajty 0xA2 (¢), 0xA3 (£), 0xA9 (©), 0xB1 (±), 0xB5 (µ) są takie same w obu kodowaniach. Jeśli są to jedyne bajty spoza ASCII, nie ma znaczenia, czy wybierzesz MacRoman, czy cp1252.
Podejście statystyczne
Policz częstotliwości znaków (NIE bajtów!) W danych, o których wiesz, że są to UTF-8. Określ najczęściej występujące znaki. Następnie użyj tych danych, aby określić, czy znaki cp1252 czy MacRoman są bardziej powszechne.
Na przykład podczas wyszukiwania, które właśnie przeprowadziłem na 100 losowych artykułach z angielskiej Wikipedii, najczęściej występującymi znakami spoza ASCII są
·•–é°®’èö—
. W oparciu o ten faktPolicz bajty sugerujące cp1252 i bajty sugerujące MacRoman i idź z tym, który jest największy.
źródło
Mozilla nsUniversalDetector (powiązania Perla: Encode :: Detect / Encode :: Detect :: Detector ) jest milion razy sprawdzona.
źródło
x-mac-cyrillic
jest obsługiwany,x-mac-hebrew
jest szczegółowo omawiany w komentarzach,x-mac-anything-else
nie ma wzmianki.Moja próba zastosowania takiej heurystyki (zakładając, że wykluczyłeś ASCII i UTF-8):
Dygresja:
Nie rób tego!!
Kompilator Java oczekuje, że nazwy plików będą zgodne z nazwami klas, więc zmiana nazw plików spowoduje, że kod źródłowy będzie niekompilowalny. Prawidłową rzeczą byłoby odgadnięcie kodowania, a następnie użycie
native2ascii
narzędzia do konwersji wszystkich znaków spoza ASCII na sekwencje ucieczki Unicode .źródło
*.text
plików.„Perl, C, Java lub Python i w tej kolejności”: ciekawe podejście :-)
„Mamy dobrą zmianę, wiedząc, czy coś jest prawdopodobnie UTF-8”: W rzeczywistości szansa, że plik zawierający znaczący tekst zakodowany w innym zestawie znaków, który używa bajtów o dużej wartości bitowej, zostanie pomyślnie zdekodowany, ponieważ UTF-8 jest znikomo mały.
Strategie UTF-8 (w najmniej preferowanym języku):
Kiedy już zdecydujesz, że nie jest to ani ASCII, ani UTF-8:
Wykrywacze zestawów znaków pochodzenia Mozilli, o których wiem, nie obsługują MacRoman, aw każdym razie nie radzą sobie dobrze z 8-bitowymi zestawami znaków, szczególnie w języku angielskim, ponieważ AFAICT polegają na sprawdzeniu, czy dekodowanie ma sens w danym język, ignorując znaki interpunkcyjne i oparty na szerokiej gamie dokumentów w tym języku.
Jak zauważyli inni, tak naprawdę masz dostępne tylko znaki interpunkcyjne o wysokim ustawieniu bitów, aby odróżnić cp1252 od macroman. Sugerowałbym wytrenowanie modelu typu Mozilla na własnych dokumentach, a nie na Szekspirze, Hansardzie czy Biblii KJV i wzięcie pod uwagę wszystkich 256 bajtów. Przypuszczam, że twoje pliki nie zawierają żadnych znaczników (HTML, XML itp.) - to mogłoby zniekształcić prawdopodobieństwo coś szokującego.
Wspomniałeś o plikach, które w większości są w formacie UTF-8, ale nie można ich zdekodować. Powinieneś także być bardzo podejrzliwy wobec:
(1) pliki, które są rzekomo zakodowane w ISO-8859-1, ale zawierają „znaki kontrolne” z zakresu od 0x80 do 0x9F włącznie ... jest to tak powszechne, że projekt standardu HTML5 nakazuje dekodować WSZYSTKIE strumienie HTML zadeklarowane jako ISO-8859 -1 przy użyciu cp1252.
(2) pliki, które dekodują OK jako UTF-8, ale wynikowy Unicode zawiera „znaki sterujące” z zakresu od U + 0080 do U + 009F włącznie ... może to wynikać z transkodowania cp1252 / cp850 (widać, że to się stało!) / Itd. pliki od „ISO-8859-1” do UTF-8.
Kontekst: Mam projekt na mokre niedzielne popołudnie, aby stworzyć oparty na Pythonie detektor zestawu znaków, który jest zorientowany na pliki (zamiast zorientowanego na sieć) i działa dobrze z 8-bitowymi zestawami znaków, w tym
legacy ** n
takimi jak cp850 i cp437. Jeszcze nie nadeszła pora największej oglądalności. Interesują mnie pliki szkoleniowe; Czy Twoje pliki ISO-8859-1 / cp1252 / MacRoman są równie „nieobciążone”, jak oczekujesz od czyjegoś rozwiązania kodu?źródło
Jak odkryłeś, nie ma idealnego sposobu rozwiązania tego problemu, ponieważ bez ukrytej wiedzy o tym, jakiego kodowania używa plik, wszystkie 8-bitowe kodowania są dokładnie takie same: zbiór bajtów. Wszystkie bajty są ważne dla wszystkich 8-bitowych kodowań.
Najlepsze, na co możesz liczyć, to jakiś algorytm, który analizuje bajty i na podstawie prawdopodobieństwa użycia określonego bajtu w określonym języku z określonym kodowaniem zgadnie, jakiego kodowania używa pliki. Ale to musi wiedzieć, jakiego języka używa plik, i staje się całkowicie bezużyteczne, gdy masz pliki z mieszanym kodowaniem.
Z drugiej strony, jeśli wiesz, że tekst w pliku jest napisany po angielsku, prawdopodobnie nie zauważysz żadnej różnicy niezależnie od tego, które kodowanie zdecydujesz się użyć dla tego pliku, ponieważ różnice między wszystkimi wspomnianymi kodowaniami są zlokalizowane w części kodowania, które określają znaki, które nie są zwykle używane w języku angielskim. Możesz mieć pewne problemy, gdy tekst używa specjalnego formatowania lub specjalnych wersji znaków interpunkcyjnych (na przykład CP1252 ma kilka wersji cudzysłowów), ale jeśli chodzi o istotę tekstu, prawdopodobnie nie będzie żadnych problemów.
źródło
Jeśli potrafisz wykryć każde kodowanie Z WYJĄTKIEM makromany, logiczne byłoby założenie, że te, których nie można odszyfrować, są w makromie. Innymi słowy, po prostu zrób listę plików, których nie można przetworzyć i postępuj z nimi tak, jakby były makromą.
Innym sposobem sortowania tych plików byłoby utworzenie programu opartego na serwerze, który pozwala użytkownikom zdecydować, które kodowanie nie jest zniekształcone. Oczywiście byłoby to w firmie, ale przy 100 pracownikach wykonujących po kilku każdego dnia będziesz mieć tysiące plików zrobionych w mgnieniu oka.
Wreszcie, czy nie byłoby lepiej po prostu przekonwertować wszystkie istniejące pliki do jednego formatu i wymagać, aby nowe pliki były w tym formacie.
źródło
Obecnie piszę program, który tłumaczy pliki na XML. Musi automatycznie wykrywać typ każdego pliku, co jest nadzbiorem problemu określenia kodowania pliku tekstowego. Do określenia kodowania używam metody bayesowskiej. Oznacza to, że mój kod klasyfikacyjny oblicza prawdopodobieństwo (prawdopodobieństwo), że plik tekstowy ma określone kodowanie dla wszystkich kodowań, które rozumie. Następnie program wybiera najbardziej prawdopodobny dekoder. Podejście bayesowskie działa w ten sposób dla każdego kodowania.
Okazuje się, że Bayesa Twierdzenie staje się bardzo łatwe do zrobienia, jeśli zamiast obliczania prawdopodobieństw, obliczyć zawartość informacyjną , która jest logarytm z kursem :
info = log(p / (1.0 - p))
.Będziesz musiał obliczyć prawdopodobieństwo initail priori i korelacje, badając korpus plików, które zostały ręcznie sklasyfikowane.
źródło