Menu
Za darmo
Rejestracja
dom  /  Oprogramowanie układowe/ Definicja kodowania tekstu w PHP i Pythonie. Określanie kodowania tekstu w PHP — przegląd istniejących rozwiązań oraz kolejny rower A co z PHP

Określanie kodowania tekstu w PHP i Pythonie. Określanie kodowania tekstu w PHP — przegląd istniejących rozwiązań oraz kolejny rower A co z PHP

Pojawił się problem: jak szybko określić kodowanie ciągu tekstowego względem UTF-8 Coraz częściej trzeba pracować z ciągami znaków w kodowaniu UNICODE.

Poniżej znajduje się funkcja sprawdzająca, czy kodowanie UNICODE (UTF-8) wymaga konwersji na kodowanie WINDOWS (win-1251)

Funkcja daje dość dokładną odpowiedź, chociaż nie jest oparta na konwersji kod po znaku.

funkcjonować wykryj_my_utf($s)( $s=urlencode($s); // dodatkowa operacja w niektórych przypadkach (skomentuj) $res="0"; $j=strlen($s); $s2=strtoupper($s) ) ; $s2=str_replace("%D0","",$s2); $s2=str_replace("%D1","",$s2); $k=strlen($s2); $m=1; if ($k>0)( $m=$j/$k; if (($m>1.2)&&($m

W skrócie - opis funkcji wykryj_my_utf():

  • konwertuj (ciąg do specjalnego formatu)
  • obliczyć długość ciągu wejściowego
  • zamień wszystkie litery w łańcuchu na wielkie
  • usuń określone kody %D0 i %D1
  • oblicz długość nowej linii
  • uzyskaj stosunek starej linii do nowej

Jeśli ten współczynnik wynosi 1 lub jest do niego zbliżony, istnieje podejrzenie, że ciąg przychodzący nie został zakodowany w UNICODE. Jeśli ten współczynnik mieści się w przedziale od 1,2 do 2,2, to możesz bezpiecznie ponownie zakodować ciąg znaków do kodowania WINDOWS win-1251.

Na wyjściu funkcji mamy odpowiednio 0 lub 1, a nie UNICODE lub UNICODE.

Przykłady wykonania funkcji:

Ciąg wejściowy: R°R?RyoR?R°S+RyoRyo R? imageready Przekonwertowany ciąg znaków: %D0%BF%D0%BE%D1%80%D1%8F%D0%B4%D0%BE%D0%BA %D1%81%D0%BE%D0%B7%D0%B4%D0 %B0%D0%BD%D0%B8%D1%8F %D0%B0%D0%BD%D0%B8%D0%BC%D0%B0%D1%86%D0%B8%D0%B8%20%D0 %B2 imageready Wynik funkcji: 1 Zakodowana fraza: jak stworzyć animację w imageready

Ciąg wejściowy: R?C?R?R?R?C

Ciąg wejściowy:

Ciąg wejściowy: przewodnik rysunkowy Przekonwertowany ciąg znaków: %EF%EE%F1%EE%E1%E8%E5 %EF%EE %F0%E8%F1%EE%E2%E0%ED%E8%FE Wynik funkcji: 0 Zakodowana fraza : Samouczek rysowania Ten algorytm dobrze radzi sobie z różnymi łańcuchami przychodzącymi w ramach usługi statystyki konwersji w wyszukiwarce.

Ciekawe materiały na stronie:

  • Artykuł o zainteresowaniach wyszukiwania witryny. Być może niektóre materiały są już nieaktualne od 10 lat, ale na niektóre punkty warto zwrócić uwagę.

  • Twój pogląd na problem wymiany hiperłączy między witryną dawcy a witrynami odbiorców.

  • Kolejny lifehack. Pokonaliśmy nieuczciwych graczy w grze „Balda”. Duża baza słów, którą można łatwo rozbudować.

W obliczu zadania - automatyczne wykrywanie strony/tekstu/cokolwiek kodowania. Zadanie nie jest nowe, a wiele rowerów już wynaleziono. Artykuł zawiera mały przegląd tego, co zostało znalezione w sieci - plus własną ofertę, jak mi się wydaje, godną rozwiązania.

1. Dlaczego nie mb_detect_encoding() ?

Krótko mówiąc, to nie działa.

Obejrzyjmy:
// Na wejściu - rosyjski tekst zakodowany w CP1251 $string = iconv("UTF-8", "Windows-1251", "Podszedł do Anny Pawłownej, pocałował ją w rękę, oferując jej swoją wyperfumowaną i lśniącą łysinę i spokojnie usiadł na kanapie."); // Zobaczmy, co daje nam md_detect_encoding(). Najpierw $strict = FALSE var_dump(mb_detect_encoding($string, array("UTF-8"))); // UTF-8 var_dump(mb_detect_encoding($string, array("UTF-8", "Windows-1251"))); // Windows-1251 var_dump(mb_detect_encoding($string, array("UTF-8", "KOI8-R"))); // KOI8-R var_dump(mb_detect_encoding($string, array("UTF-8", "Windows-1251", "KOI8-R"))); // FAŁSZ var_dump(mb_detect_encoding($string, array("UTF-8", "ISO-8859-5"))); // ISO-8859-5 var_dump(mb_detect_encoding($string, array("UTF-8", "Windows-1251", "KOI8-R", "ISO-8859-5"))); // ISO-8859-5 // Teraz $strict = TRUE var_dump(mb_detect_encoding($string, array("UTF-8"), TRUE)); // FAŁSZ var_dump(mb_detect_encoding($string, array("UTF-8", "Windows-1251"), TRUE)); // FAŁSZ var_dump(mb_detect_encoding($string, array("UTF-8", "KOI8-R"), TRUE)); // FAŁSZ var_dump(mb_detect_encoding($string, array("UTF-8", "Windows-1251", "KOI8-R"), TRUE)); // FAŁSZ var_dump(mb_detect_encoding($string, array("UTF-8", "ISO-8859-5"), TRUE)); // ISO-8859-5 var_dump(mb_detect_encoding($string, array("UTF-8", "Windows-1251", "KOI8-R", "ISO-8859-5"), TRUE)); // ISO-8859-5
Jak widać, wynik to kompletny bałagan. Co robimy, gdy nie rozumiemy, dlaczego funkcja zachowuje się w ten sposób? Zgadza się, googlujemy. Znalazłem świetną odpowiedź.

Aby ostatecznie rozwiać wszelkie nadzieje związane z użyciem mb_detect_encoding(), należy sięgnąć do źródeł rozszerzenia mbstring. Więc podwiń rękawy, chodźmy:
// ext/mbstring/mbstring.c:2629 PHP_FUNCTION(mb_detect_encoding) ( ... // linia 2703 ret = mbfl_identify_encoding_name(&string, elist, size, strict); ...
Ctrl+kliknięcie:
// ext/mbstring/libmbfl/mbfl/mbfilter.c:643 const char* mbfl_identify_encoding_name(mbfl_string *string, enum mbfl_no_encoding *elist, int elistsz, int strict) ( const mbfl_encoding *encoding; encoding = mbfl_identify_encoding(string, elist, elistsz , ścisły); ...
Ctrl+kliknięcie:
// ext/mbstring/libmbfl/mbfl/mbfilter.c:557 /* * identyfikacja kodowania */ const mbfl_encoding * mbfl_identify_encoding(mbfl_string *string, enum mbfl_no_encoding *elist, int elistsz, int strict) ( ...
post pełny tekst Nie będę stosował metody, aby nie zapychać artykułu niepotrzebnymi źródłami. Zainteresowani przekonają się sami. Nas zainteresuje linia numer 593, gdzie faktycznie dokonuje się sprawdzenia, czy znak pasuje do kodowania:
// ext/mbstring/libmbfl/mbfl/mbfilter.c:593 (*filter->filter_function)(*p, filter); if (filtr->flaga) ( bad++; )
Oto główne filtry dla jednobajtowej cyrylicy:

Windows-1251 (zachowano oryginalne komentarze)
// ext/mbstring/libmbfl/filters/mbfilter_cp1251.c:142 /* to wszystko jest teraz takie brzydkie! */ static int mbfl_filt_ident_cp1251(int c, mbfl_identify_filter *filter) ( if (c >= 0x80 && c< 0xff) filter->flaga = 0; inny filtr->

KOI8-R
// ext/mbstring/libmbfl/filters/mbfilter_koi8r.c:142 static int mbfl_filt_ident_koi8r(int c, mbfl_identify_filter *filter) ( if (c >= 0x80 && c< 0xff) filter->flaga = 0; w przeciwnym razie filtr->flaga = 1; /* nie to */ zwróć c; )

ISO-8859-5 (wszystko jest ogólnie zabawne)
// ext/mbstring/libmbfl/mbfl/mbfl_ident.c:248 int mbfl_filt_ident_true(int c, mbfl_identify_filter *filter) ( return c; )
Jak widać, ISO-8859-5 zawsze zwraca PRAWDA (aby zwrócić FAŁSZ, należy ustawić filtr->flaga = 1).

Kiedy spojrzeliśmy na filtry, wszystko się ułożyło. CP1251 jest nie do odróżnienia od KOI8-R. ISO-8859-5 ogólnie, jeśli znajduje się na liście kodowań, zawsze zostanie wykryty jako poprawny.

Ogólnie porażka. Jest to zrozumiałe - tylko za pomocą kodów znaków w ogólnym przypadku nie można znaleźć kodowania, ponieważ kody te przecinają się w różnych kodowaniach.

2. Co daje Google

A Google daje wszelkiego rodzaju nędzę. Nawet źródła tutaj nie wrzucę, sami zobaczcie jeśli chcecie (usuńcie spację po http://, nie wiem jak pokazać tekst nie jako link):

http://deer.org.ua/2009/10/06/1/
http://php.su/forum/topic.php?forum=1&topic=1346

3. Habr szukaj

1) ponownie kody znaków: habrahabr.ru/blogs/php/27378/#comment_710532

2) moim zdaniem bardzo ciekawe rozwiązanie: habrahabr.ru/blogs/php/27378/#comment_1399654
Plusy i minusy w komentarzach pod linkiem. Osobiście uważam, że to rozwiązanie jest zbędne tylko do wykrywania kodowania - okazuje się za mocne. Definicja kodowania w nim - jako efekt uboczny).

4. Właściwie moja decyzja

Pomysł zrodził się podczas przeglądania drugiego linku z poprzedniej sekcji. Pomysł jest następujący: bierzemy duży rosyjski tekst, mierzymy częstotliwości różnych liter i używamy tych częstotliwości do wykrywania kodowania. Patrząc w przyszłość, od razu powiem, że będą problemy z dużymi i małymi literami. Dlatego zamieszczam przykłady częstotliwości liter (nazwijmy to „widmem”) zarówno z rozróżnianiem wielkości liter, jak i bez (w drugim przypadku do małej litery o tej samej częstotliwości dodałem jeszcze większą literę, a wszystkie duże usunąłem). W tych „spektrach” wszystkie litery o częstotliwościach mniejszych niż 0,001 i spacja są wycinane. Oto, co otrzymałem po przetworzeniu „Wojny i pokoju”:

„Widmo” uwzględniające wielkość liter:
array("o" => 0,095249209893009, "e" => 0,06836817536026, "a" => 0,067481298384992, "u" => 0,055995027400041, "n" => 0,052242744063325, ... „e” => 0,002252892226507, „N " => 0,0021318391371162, "P" => 0,0018574762967903, "f" => 0,0015961610948418, "V" => 0,0014044332975731, "O" => 0,0013188987793209 , "A" => 0,0012623590130186, "K" => 0,0011804488387602, "M" => 0,001061932790165,)

Wielkość liter nie ma znaczenia:
array("O" => 0,095249209893009, "o" => 0,095249209893009, "E" => 0,06836817536026, "e" => 0,06836817536026, "A" => 0,067481298384992, "a " => 0,067481298384992, "ORAZ" => 0,055995027400041 , "i" => 0,055995027400041, .... "C" => 0,0029893589260344, "c" => 0,0029893589260344, "u" => 0,0024649163501406, "u" => 0,00246491635 01406, "E" => 0,002252892226507, "e " => 0,002252892226507, "Ф" => 0,0015961610948418, "Ф" => 0,0015961610948418,)

Widma w różnych kodowaniach (klucze tablicowe - kody odpowiednich znaków w odpowiednim kodowaniu):

Dalej. Bierzemy tekst o nieznanym kodowaniu, dla każdego sprawdzanego kodowania znajdujemy częstotliwość bieżącego znaku i dodajemy to kodowanie do „oceny”. Kodowanie z najwyższą oceną to najprawdopodobniej kodowanie tekstu.

$encodings = array("cp1251" => wymagaj "specter_cp1251.php", "koi8r" => wymagaj "specter_koi8r.php", "iso88595" => wymagaj "specter_iso88595.php"); $enc_rates = array(); dla ($i = 0; $i< len($str); ++$i) { foreach ($encodings as $encoding =>$char_specter) ( $enc_rates[$encoding] += $char_specter)]; ) var_dump($enc_rates);
Nawet nie próbuj samodzielnie uruchamiać tego kodu - to nie zadziała. Możesz myśleć o tym jak o pseudokodzie - pominąłem szczegóły, aby nie zaśmiecać artykułu. $char_specter to tylko tablice, do których odwołuje się Pastebin.

wyniki
Wiersze tabeli to kodowanie tekstu, kolumny to zawartość tablicy $enc_rates.

1) $str = "tekst rosyjski";
0,441 | 0,020 | 0,085 | Windows-1251
0,049 | 0,441 | 0,166 | KOI8-R
0,133 | 0,092 | 0,441 | ISO-8859-5

Wszystko w porządku. Prawdziwe kodowanie ma już 4 razy wyższą ocenę niż pozostałe - i to na tak krótkim tekście. W przypadku dłuższych tekstów proporcje będą mniej więcej takie same.


cp1251 | koi8r | iso88595 |
0,013 | 0,705 | 0,331 | Windows-1251
0,649 | 0,013 | 0,201 | KOI8-R
0,007 | 0,392 | 0,013 | ISO-8859-5

Ups! Pełna owsianka. Ponieważ duże litery w CP1251 zwykle odpowiadają małym literom w KOI8-R. A małe litery są używane z kolei znacznie częściej niż duże. Więc definiujemy łańcuch caps w CP1251 jako KOI8-R.
Próba zrobienia tego bez rozróżniania wielkości liter („spektra” bez rozróżniania wielkości liter)

1) $str = "tekst rosyjski";
cp1251 | koi8r | iso88595 |
0,477 | 0,342 | 0,085 | Windows-1251
0,315 | 0,477 | 0,207 | KOI8-R
0,216 | 0,321 | 0,477 | ISO-8859-5

2) $str = "STRING TEKSTU W JĘZYKU ROSYJSKIM";
cp1251 | koi8r | iso88595 |
1,074 | 0,705 | 0,465 | Windows-1251
0,649 | 1,074 | 0,201 | KOI8-R
0,331 | 0,392 | 1,074 | ISO-8859-5

Jak widać, poprawne kodowanie konsekwentnie prowadzi zarówno w przypadku „widm” rozróżniających wielkość liter (jeśli łańcuch zawiera niewielką liczbę wielkich liter), jak i tych, które nie uwzględniają wielkości liter. W drugim przypadku, przy tych bez rozróżniania wielkości liter, lider nie jest oczywiście tak pewny siebie, ale dość stabilny nawet na małych strunach. Możesz także bawić się wagami liter - na przykład spraw, aby były nieliniowe w odniesieniu do częstotliwości.

5. Wniosek

Temat nie obejmuje pracy z UTF-8 - nie ma tu zasadniczej różnicy, poza tym, że pobieranie kodów znaków i dzielenie łańcucha na znaki będzie nieco dłuższe/bardziej skomplikowane.
Pomysły te można oczywiście rozszerzyć nie tylko na kodowanie cyrylicy - pytanie dotyczy tylko „widm” odpowiednich języków/kodowań.

PS Jeśli będzie to bardzo potrzebne/interesujące, to opublikuję drugą część w pełni działającej biblioteki na GitHubie. Chociaż uważam, że dane w poście są wystarczające, aby szybko napisać taką bibliotekę i dostosować ją do własnych potrzeb - „widmo” dla języka rosyjskiego jest ułożone, można je łatwo przenieść do wszystkich niezbędnych kodowań.

Pomysł - Loteria

Nie wpadłem na pomysł zdobycia kodowania, ale niestety nie mogę teraz powiedzieć autorowi, ponieważ było to około 4 lata temu, a skąd mam te informacje, już dawno zapomniałem. Autor zaproponował wariant definicji i pokazał przykład dla 1-2 kodowań w języku Python. Prostota jego rozwiązania nie odrzuciła mnie na bok i rozwinąłem go do pożądanego rezultatu.
Istota idei tkwi w samych tablicach kodowania. Jak wiadomo, każde kodowanie zawiera własną tablicę kodów, a do każdego znaku kodowania przypisana jest określona wartość. Nie będę tutaj pokazywać tabel kodowania, teraz dość łatwo je znaleźć w Internecie.
Zasada wykonania jest następująca:
  1. Tworzona jest zmienna tablicowa do przechowywania wyniku „analizy” sprawdzanego tekstu. Każdy element tablicy będzie zawierał wynik dla określonego kodowania.
  2. Tekst otrzymany na wejściu funkcji jest sortowany znak po znaku.
  3. Liczba porządkowa (wartość tego znaku) jest pobierana z każdego znaku i porównywana z zakresem kodowania.
  4. Jeśli wartość przypada na wielką literę (dużą literę), element tablicy przechowujący wynik tego kodowania jest ustawiany na 1.
  5. Jeśli wartość przypada na małą (małą) literę, element tablicy przechowujący wynik tego kodowania jest ustawiany na 3.
  6. To kodowanie, a dokładniej ten element tablicy, który przechowuje wynik jego kodowania, który zdobył najwięcej punktów, jest najprawdopodobniej oryginalnym kodowaniem.
Ten algorytm jest ważny dla kodowań jednobajtowych, takich jak KOI-8, CP1251 (Windows-1251) i innych. Jednak w przypadku kodowania dwubajtowego (w moim przypadku UTF-8) takie podejście da błędny wynik. Na początek próbowałem rozwiązać ten problem, dodając 5 dla wielkich liter i 7 dla małych liter. Wynik był lepszy, ale nadal występowały błędy rozpoznawania. Po kilku eksperymentach doszedłem do wniosku, że dla poprawnej definicji UTF, z dużymi znakami, do wyniku należy dodać 10, dla małych 14, czyli 2 razy więcej niż moje początkowe założenie. Jednak dla lepszego wizualnego zrozumienia kodu zostawiłem odpowiednio 5 i 7 dla znaków UTF, a już podczas sprawdzania wartości te są zwiększane o 2 i dodawane do wyniku.
To w zasadzie cały algorytm. I bez dodatkowych kłopotów.
Najwięcej czasu na implementację tej funkcji poświęcono oczywiście na szukanie tablic kodów i poprawne ułożenie zakresów. Nie tylko dość trudno było znaleźć rzeczywistą tabelę kodów w czasie, gdy po raz pierwszy pisałem tę funkcję, ale także zakresy w nich znaków przeskakują losowo. Jednak potem zdecydowałem się na najbardziej odpowiednie (do dziś) kodowania: UTF-8, CP1251, KOI8-R, IBM866, ISO-8859-5 i MAC. Jeśli te kodowania nie są dla Ciebie wystarczające, możesz oprzeć się na ten algorytm dodaj kod.

Od słów do praktyki

Właściwie cały kod funkcji w Pythonie wygląda tak:

Kodowania = ( "UTF-8": "utf-8", "CP1251": "windows-1251", "KOI8-R": "koi8-r", "IBM866": "ibm866", "ISO-8859- 5": "iso-8859-5", "MAC": "mac", ) """ Definicja kodowania tekstu """ def get_codepage(str = None): wielkie litery = 1 mała litera = 3 utfupper = 5 utflower = 7 stron kodowych = () for enc in encodings.keys(): codepages = 0 jeśli str nie jest None i len(str) > 0: last_simb = 0 for simb in str: simb_ord = ord(simb) """nierosyjskie znaki" "" jeśli simb_ord< 128 or simb_ord >256: kontynuuj """UTF-8""" jeśli last_simb == 208 i (143< simb_ord < 176 or simb_ord == 129): codepages["UTF-8"] += (utfupper * 2) if (last_simb == 208 and (simb_ord == 145 or 175 < simb_ord < 192)) \ or (last_simb == 209 and (127 < simb_ord < 144)): codepages["UTF-8"] += (utflower * 2) """CP1251""" if 223 < simb_ord < 256 or simb_ord == 184: codepages["CP1251"] += lowercase if 191 < simb_ord < 224 or simb_ord == 168: codepages["CP1251"] += uppercase """KOI8-R""" if 191 < simb_ord < 224 or simb_ord == 163: codepages["KOI8-R"] += lowercase if 222 < simb_ord < 256 or simb_ord == 179: codepages["KOI8-R"] += uppercase """IBM866""" if 159 < simb_ord < 176 or 223 < simb_ord < 241: codepages["IBM866"] += lowercase if 127 < simb_ord < 160 or simb_ord == 241: codepages["IBM866"] += uppercase """ISO-8859-5""" if 207 < simb_ord < 240 or simb_ord == 161: codepages["ISO-8859-5"] += lowercase if 175 < simb_ord < 208 or simb_ord == 241: codepages["ISO-8859-5"] += uppercase """MAC""" if 221 < simb_ord < 255: codepages["MAC"] += lowercase if 127 < simb_ord < 160: codepages["MAC"] += uppercase last_simb = simb_ord idx = "" max = 0 for item in codepages: if codepages >max:max = idx stron kodowych = idx zwrotu elementu
Przykład wywołania funkcji

Drukuj kodowania

A co z PHPem

Nie było trudno przepisać gotową funkcję z Pythona na PHP. Z wyglądu praktycznie nie różni się od swojego przodka w Pythonie:

/** * Zdefiniuj kodowanie tekstu * @param String $text Text * @return String Kodowanie tekstu */ function get_codepage($text = "") ( if (!empty($text)) ( $utflower = 7; $utfupper = 5; $małe litery = 3; $duże litery = 1; $last_simb = 0; $charsets = array("UTF-8" => 0, "CP1251" => 0, "KOI8-R" => 0, "IBM866" => 0, "ISO-8859-5" => 0, "MAC" => 0,); for ($a = 0; $a< strlen($text); $a++) { $char = ord($text[$a]); // non-russian characters if ($char<128 || $char>256) kontynuuj; // UTF-8 if (($last_simb==208) && (($char>143 && $char<176) || $char==129)) $charsets["UTF-8"] += ($utfupper * 2); if ((($last_simb==208) && (($char>175 && $char<192) || $char==145)) || ($last_simb==209 && $char>127 && $znak<144)) $charsets["UTF-8"] += ($utflower * 2); // CP1251 if (($char>223 && $znak<256) || $char==184) $charsets["CP1251"] += $lowercase; if (($char>191 && $znak<224) || $char==168) $charsets["CP1251"] += $uppercase; // KOI8-R if (($char>191 && $znak<224) || $char==163) $charsets["KOI8-R"] += $lowercase; if (($char>222 && $znak<256) || $char==179) $charsets["KOI8-R"] += $uppercase; // IBM866 if (($char>159 && $znak<176) || ($char>223 && $znak<241)) $charsets["IBM866"] += $lowercase; if (($char>127 && $znak<160) || $char==241) $charsets["IBM866"] += $uppercase; // ISO-8859-5 if (($char>207 && $znak<240) || $char==161) $charsets["ISO-8859-5"] += $lowercase; if (($char>175 && $char<208) || $char==241) $charsets["ISO-8859-5"] += $uppercase; // MAC if ($char>221 && $znak<255) $charsets["MAC"] += $lowercase; if ($char>127 && $znak<160) $charsets["MAC"] += $uppercase; $last_simb = $char; } arsort($charsets); return key($charsets); } }
Przykład wywołania funkcji

echo get_codepage(file_get_contents("test.txt"));

Likwiduj, czyli nie przeszkadzaj maszynie w pracy

Nie należy próbować przeprowadzać testów zderzeniowych tej funkcji. Z algorytmu jasno wynika, że ​​im mniej tekstu otrzyma jako dane wejściowe, tym bardziej prawdopodobne jest, że funkcja nieprawidłowo rozpozna kodowanie. Z drugiej strony podawanie tomów Lwa Tołstoja również nie ma sensu: ta metoda świetnie sobie radzi z małym zdaniem o długości 100-200 znaków. I choć w przykładach wywołań input wysyłałem całą zawartość pewnego pliku „test.txt”, który zakładał, że istnieje tekst, którego kodowanie trzeba ustalić, to mały fragment tekstu może (i powinien) być przekazywane na wejście funkcji.
Perwersje z mieszanymi wielkimi i małymi literami generalnie nie są odpowiednie w tym przypadku, ponieważ ta metoda została napisana do zwykłego zwykłego zadania z mniej więcej piśmiennym rosyjskim. A takie eksperymenty najczęściej przypominają mi dowcip:

Rosyjski zakład obróbki drewna zakupił japońską maszynę. Rosyjscy robotnicy zebrali się wokół niego i dowiedzmy się, jak działa. Jeden wziął deskę, włożył ją do niej. Jednostka: zzzzzzzzzzzzzzzin... Przy wyjściu gotowy taboret. Chłopaki: Nic dla siebie!!! Jednostka na wyświetlaczu: cóż, co o tym sądzisz? Inny wziął nieociosaną kłodę i włożył ją do jednostki. Jednostka: zzzzzzzzzzzzzzz... Przy wyjściu stoi gotowy rzeźbiony kredens. Chłopaki: Nic!!! Jednostka na wyświetlaczu: cóż, co o tym sądzisz? Trzeci mężczyzna nie wytrzymał, wyciągnął skądś szynę i wsadził ją do jednostki. Jednostka: drrrrr-tyh-tyh-tyh... Dymiło i na wyświetlaczu napis: Nic do siebie!!! Faceci: Cóż, co myślisz!
Tak więc do takich przewrotnych testów najprawdopodobniej będziesz potrzebować przewrotnego algorytmu, którym ta funkcja nie jest. A z praktyki powiem, że w ciągu 4 lat stosowania ta metoda nigdy mnie nie zawiodła i zawsze dawała właściwy rezultat.
Mam nadzieję, że mój artykuł przyda się komuś.
Dziękuję za uwagę.

Korzystając z całości lub części treści, nie zapomnij o linku do źródła, czyli do mojego bloga.

W obliczu zadania - automatyczne wykrywanie strony/tekstu/cokolwiek kodowania. Zadanie nie jest nowe, a wiele rowerów już wynaleziono. Artykuł zawiera mały przegląd tego, co zostało znalezione w sieci - plus własną ofertę, jak mi się wydaje, godną rozwiązania.

1. Dlaczego nie mb_detect_encoding() ?

Krótko mówiąc, to nie działa.

Obejrzyjmy:
// Na wejściu - rosyjski tekst zakodowany w CP1251 $string = iconv("UTF-8", "Windows-1251", "Podszedł do Anny Pawłownej, pocałował ją w rękę, oferując jej swoją wyperfumowaną i lśniącą łysinę i spokojnie usiadł na kanapie."); // Zobaczmy, co daje nam md_detect_encoding(). Najpierw $strict = FALSE var_dump(mb_detect_encoding($string, array("UTF-8"))); // UTF-8 var_dump(mb_detect_encoding($string, array("UTF-8", "Windows-1251"))); // Windows-1251 var_dump(mb_detect_encoding($string, array("UTF-8", "KOI8-R"))); // KOI8-R var_dump(mb_detect_encoding($string, array("UTF-8", "Windows-1251", "KOI8-R"))); // FAŁSZ var_dump(mb_detect_encoding($string, array("UTF-8", "ISO-8859-5"))); // ISO-8859-5 var_dump(mb_detect_encoding($string, array("UTF-8", "Windows-1251", "KOI8-R", "ISO-8859-5"))); // ISO-8859-5 // Teraz $strict = TRUE var_dump(mb_detect_encoding($string, array("UTF-8"), TRUE)); // FAŁSZ var_dump(mb_detect_encoding($string, array("UTF-8", "Windows-1251"), TRUE)); // FAŁSZ var_dump(mb_detect_encoding($string, array("UTF-8", "KOI8-R"), TRUE)); // FAŁSZ var_dump(mb_detect_encoding($string, array("UTF-8", "Windows-1251", "KOI8-R"), TRUE)); // FAŁSZ var_dump(mb_detect_encoding($string, array("UTF-8", "ISO-8859-5"), TRUE)); // ISO-8859-5 var_dump(mb_detect_encoding($string, array("UTF-8", "Windows-1251", "KOI8-R", "ISO-8859-5"), TRUE)); // ISO-8859-5
Jak widać, wynik to kompletny bałagan. Co robimy, gdy nie rozumiemy, dlaczego funkcja zachowuje się w ten sposób? Zgadza się, googlujemy. Znalazłem świetną odpowiedź.

Aby ostatecznie rozwiać wszelkie nadzieje związane z użyciem mb_detect_encoding(), należy sięgnąć do źródeł rozszerzenia mbstring. Więc podwiń rękawy, chodźmy:
// ext/mbstring/mbstring.c:2629 PHP_FUNCTION(mb_detect_encoding) ( ... // linia 2703 ret = mbfl_identify_encoding_name(&string, elist, size, strict); ...
Ctrl+kliknięcie:
// ext/mbstring/libmbfl/mbfl/mbfilter.c:643 const char* mbfl_identify_encoding_name(mbfl_string *string, enum mbfl_no_encoding *elist, int elistsz, int strict) ( const mbfl_encoding *encoding; encoding = mbfl_identify_encoding(string, elist, elistsz , ścisły); ...
Ctrl+kliknięcie:
// ext/mbstring/libmbfl/mbfl/mbfilter.c:557 /* * identyfikacja kodowania */ const mbfl_encoding * mbfl_identify_encoding(mbfl_string *string, enum mbfl_no_encoding *elist, int elistsz, int strict) ( ...
Nie będę zamieszczał pełnego tekstu metody, aby nie zapychać artykułu niepotrzebnymi źródłami. Zainteresowani przekonają się sami. Nas zainteresuje linia numer 593, gdzie faktycznie dokonuje się sprawdzenia, czy znak pasuje do kodowania:
// ext/mbstring/libmbfl/mbfl/mbfilter.c:593 (*filter->filter_function)(*p, filter); if (filtr->flaga) ( bad++; )
Oto główne filtry dla jednobajtowej cyrylicy:

Windows-1251 (zachowano oryginalne komentarze)
// ext/mbstring/libmbfl/filters/mbfilter_cp1251.c:142 /* to wszystko jest teraz takie brzydkie! */ static int mbfl_filt_ident_cp1251(int c, mbfl_identify_filter *filter) ( if (c >= 0x80 && c< 0xff) filter->flaga = 0; inny filtr->

KOI8-R
// ext/mbstring/libmbfl/filters/mbfilter_koi8r.c:142 static int mbfl_filt_ident_koi8r(int c, mbfl_identify_filter *filter) ( if (c >= 0x80 && c< 0xff) filter->flaga = 0; w przeciwnym razie filtr->flaga = 1; /* nie to */ zwróć c; )

ISO-8859-5 (wszystko jest ogólnie zabawne)
// ext/mbstring/libmbfl/mbfl/mbfl_ident.c:248 int mbfl_filt_ident_true(int c, mbfl_identify_filter *filter) ( return c; )
Jak widać, ISO-8859-5 zawsze zwraca PRAWDA (aby zwrócić FAŁSZ, należy ustawić filtr->flaga = 1).

Kiedy spojrzeliśmy na filtry, wszystko się ułożyło. CP1251 jest nie do odróżnienia od KOI8-R. ISO-8859-5 ogólnie, jeśli znajduje się na liście kodowań, zawsze zostanie wykryty jako poprawny.

Ogólnie porażka. Jest to zrozumiałe - tylko za pomocą kodów znaków w ogólnym przypadku nie można znaleźć kodowania, ponieważ kody te przecinają się w różnych kodowaniach.

2. Co daje Google

A Google daje wszelkiego rodzaju nędzę. Nawet źródła tutaj nie wrzucę, sami zobaczcie jeśli chcecie (usuńcie spację po http://, nie wiem jak pokazać tekst nie jako link):

http://deer.org.ua/2009/10/06/1/
http://php.su/forum/topic.php?forum=1&topic=1346

3. Habr szukaj

1) ponownie kody znaków:

2) moim zdaniem bardzo ciekawe rozwiązanie:
Plusy i minusy w komentarzach pod linkiem. Osobiście uważam, że to rozwiązanie jest zbędne tylko do wykrywania kodowania - okazuje się za mocne. Definicja kodowania w nim - jako efekt uboczny).

4. Właściwie moja decyzja

Pomysł zrodził się podczas przeglądania drugiego linku z poprzedniej sekcji. Pomysł jest następujący: bierzemy duży rosyjski tekst, mierzymy częstotliwości różnych liter i używamy tych częstotliwości do wykrywania kodowania. Patrząc w przyszłość, od razu powiem, że będą problemy z dużymi i małymi literami. Dlatego zamieszczam przykłady częstotliwości liter (nazwijmy to „widmem”) zarówno z rozróżnianiem wielkości liter, jak i bez (w drugim przypadku do małej litery o tej samej częstotliwości dodałem jeszcze większą literę, a wszystkie duże usunąłem). W tych „spektrach” wszystkie litery o częstotliwościach mniejszych niż 0,001 i spacja są wycinane. Oto, co otrzymałem po przetworzeniu „Wojny i pokoju”:

„Widmo” uwzględniające wielkość liter:
array("o" => 0,095249209893009, "e" => 0,06836817536026, "a" => 0,067481298384992, "u" => 0,055995027400041, "n" => 0,052242744063325, ... „e” => 0,002252892226507, „N " => 0,0021318391371162, "P" => 0,0018574762967903, "f" => 0,0015961610948418, "V" => 0,0014044332975731, "O" => 0,0013188987793209 , "A" => 0,0012623590130186, "K" => 0,0011804488387602, "M" => 0,001061932790165,)

Wielkość liter nie ma znaczenia:
array("O" => 0,095249209893009, "o" => 0,095249209893009, "E" => 0,06836817536026, "e" => 0,06836817536026, "A" => 0,067481298384992, "a " => 0,067481298384992, "ORAZ" => 0,055995027400041 , "i" => 0,055995027400041, .... "C" => 0,0029893589260344, "c" => 0,0029893589260344, "u" => 0,0024649163501406, "u" => 0,00246491635 01406, "E" => 0,002252892226507, "e " => 0,002252892226507, "Ф" => 0,0015961610948418, "Ф" => 0,0015961610948418,)

Widma w różnych kodowaniach (klucze tablicowe - kody odpowiednich znaków w odpowiednim kodowaniu):

Dalej. Bierzemy tekst o nieznanym kodowaniu, dla każdego sprawdzanego kodowania znajdujemy częstotliwość bieżącego znaku i dodajemy to kodowanie do „oceny”. Kodowanie z najwyższą oceną to najprawdopodobniej kodowanie tekstu.

$encodings = array("cp1251" => wymagaj "specter_cp1251.php", "koi8r" => wymagaj "specter_koi8r.php", "iso88595" => wymagaj "specter_iso88595.php"); $enc_rates = array(); dla ($i = 0; $i< len($str); ++$i) { foreach ($encodings as $encoding =>$char_specter) ( $enc_rates[$encoding] += $char_specter)]; ) var_dump($enc_rates);
Nawet nie próbuj samodzielnie uruchamiać tego kodu - to nie zadziała. Możesz myśleć o tym jak o pseudokodzie - pominąłem szczegóły, aby nie zaśmiecać artykułu. $char_specter to tylko tablice, do których odwołuje się Pastebin.

wyniki
Wiersze tabeli to kodowanie tekstu, kolumny to zawartość tablicy $enc_rates.

1) $str = "tekst rosyjski";
0,441 | 0,020 | 0,085 | Windows-1251
0,049 | 0,441 | 0,166 | KOI8-R
0,133 | 0,092 | 0,441 | ISO-8859-5

Wszystko w porządku. Prawdziwe kodowanie ma już 4 razy wyższą ocenę niż pozostałe - i to na tak krótkim tekście. W przypadku dłuższych tekstów proporcje będą mniej więcej takie same.


cp1251 | koi8r | iso88595 |
0,013 | 0,705 | 0,331 | Windows-1251
0,649 | 0,013 | 0,201 | KOI8-R
0,007 | 0,392 | 0,013 | ISO-8859-5

Ups! Pełna owsianka. Ponieważ duże litery w CP1251 zwykle odpowiadają małym literom w KOI8-R. A małe litery są używane z kolei znacznie częściej niż duże. Więc definiujemy łańcuch caps w CP1251 jako KOI8-R.
Próba zrobienia tego bez rozróżniania wielkości liter („spektra” bez rozróżniania wielkości liter)

1) $str = "tekst rosyjski";
cp1251 | koi8r | iso88595 |
0,477 | 0,342 | 0,085 | Windows-1251
0,315 | 0,477 | 0,207 | KOI8-R
0,216 | 0,321 | 0,477 | ISO-8859-5

2) $str = "STRING TEKSTU W JĘZYKU ROSYJSKIM";
cp1251 | koi8r | iso88595 |
1,074 | 0,705 | 0,465 | Windows-1251
0,649 | 1,074 | 0,201 | KOI8-R
0,331 | 0,392 | 1,074 | ISO-8859-5

Jak widać, poprawne kodowanie konsekwentnie prowadzi zarówno w przypadku „widm” rozróżniających wielkość liter (jeśli łańcuch zawiera niewielką liczbę wielkich liter), jak i tych, które nie uwzględniają wielkości liter. W drugim przypadku, przy tych bez rozróżniania wielkości liter, lider nie jest oczywiście tak pewny siebie, ale dość stabilny nawet na małych strunach. Możesz także bawić się wagami liter - na przykład spraw, aby były nieliniowe w odniesieniu do częstotliwości.

5. Wniosek

Temat nie obejmuje pracy z UTF-8 - nie ma tu zasadniczej różnicy, poza tym, że pobieranie kodów znaków i dzielenie łańcucha na znaki będzie nieco dłuższe/bardziej skomplikowane.
Pomysły te można oczywiście rozszerzyć nie tylko na kodowanie cyrylicy - pytanie dotyczy tylko „widm” odpowiednich języków/kodowań.

PS Jeśli będzie to bardzo potrzebne/interesujące, to opublikuję drugą część w pełni działającej biblioteki na GitHubie. Chociaż uważam, że dane w poście są wystarczające, aby szybko napisać taką bibliotekę i dostosować ją do własnych potrzeb - „widmo” dla języka rosyjskiego jest ułożone, można je łatwo przenieść do wszystkich niezbędnych kodowań.

Czytam dużo tekstów z różnych kanałów RSS i umieszczam je w swojej bazie danych.

Oczywiście w kanałach stosuje się kilka różnych kodowań znaków, np. UTF-8 i ISO-8859-1.

Niestety czasami występują problemy z kodowaniem tekstu. Przykład:

1) „ß” w „Fußball” powinno wyglądać w mojej bazie danych tak: „Ÿ”. Jeśli jest to „Ÿ”, wyświetla się poprawnie.

2) Czasami „ß” w „Fußball” wygląda w mojej bazie danych tak: „ß”. Wtedy jest oczywiście wyświetlany niepoprawnie.

3) W innych przypadkach "ß" jest zapisywane jako "ß" - czyli bez żadnych zmian. Wtedy też wyświetla się niepoprawnie.

Co mogę zrobić, aby uniknąć przypadków 2 i 3?

Jak mogę zrobić to samo kodowanie, najlepiej UTF-8? Kiedy powinienem używać utf8_encode(), kiedy powinienem używać utf8_decode() (jasne jest, jaki jest efekt, ale kiedy powinienem używać funkcji?), a kiedy powinienem coś zrobić z danymi wejściowymi?

Czy możesz mi pomóc i powiedzieć, jak zrobić to samo kodowanie? Być może z funkcją mb-detect-encoding()? Czy mogę napisać taką funkcję? Więc moje problemy to: 1) Jak dowiedzieć się, jakie kodowanie jest używane w tekście 2) Jak przekonwertować go na UTF-8 - niezależnie od starego kodowania

EDYTOWAĆ: Czy taka funkcja będzie działać?

Funkcja correct_encoding($text) ( $current_encoding = mb_detect_encoding($text, "auto"); $text = iconv($current_encoding, "UTF-8", $text); return $text; )

Testowałem to, ale to nie działa. Co z nim nie tak?

24 odpowiedzi

Jeśli zastosujesz utf8_encode() do już istniejącego łańcucha UTF8, zwróci on zniekształcone wyjście UTF8.

Stworzyłem funkcję, która rozwiązuje wszystkie te problemy. Nazywa się to Encoding::toUTF8().

Nie musisz wiedzieć, jakie jest kodowanie twoich łańcuchów. Może to być Latin1 (iso 8859-1), Windows-1252 lub UTF8 albo ciąg znaków może je zawierać. Encoding::toUTF8() przekonwertuje wszystko na UTF8.

Zrobiłem to, ponieważ usługa dawała mi strumień danych, wszystkie pomieszane, mieszając UTF8 i Latin1 na tej samej linii.

Stosowanie:

Require_once("Kodowanie.php"); użyj \ForceUTF8\Encoding; // Teraz ma przestrzeń nazw. $utf8_string = Kodowanie::toUTF8($utf8_or_latin1_or_mixed_string); $latin1_string = Kodowanie::toLatin1($utf8_or_latin1_or_mixed_string);

Dodałem inną funkcję, Encoding::fixUFT8(), która naprawi każdy łańcuch UTF8, który wygląda na zniekształcony.

Stosowanie:

Require_once("Kodowanie.php"); użyj \ForceUTF8\Encoding; // Teraz ma przestrzeń nazw. $utf8_string = Kodowanie::fixUTF8($garbled_utf8_string);

Echo Encoding::fixUTF8("Fédération Camerounaise de Football"); echo Encoding::fixUTF8("Fédération Camerounaise de Football"); echo Encoding::fixUTF8("FÃÂédération Camerounaise de Football"); echo Encoding::fixUTF8("Federacja Kamerunu Piłki Nożnej");

Fédération Camerounaise de Football Fédération Camerounaise de Football Fédération Camerounaise de Football Fédération Camerounaise de Football

Aktualizacja: przekonwertowałem funkcję (forceUTF8) na rodzinę funkcji statycznych w klasie Encoding. Nowa cecha- Kodowanie::toUTF8().

Najpierw musisz określić, które kodowanie zostało użyte. Ponieważ analizujesz kanały RSS (być może przez HTTP), musisz odczytać kodowanie z parametru charset pola Content-Type nagłówka HTTP. Jeśli go brakuje, odczytaj kodowanie z atrybutu encoding instrukcji przetwarzania. Jeśli tego też brakuje, użyj UTF-8 zgodnie z definicją w specyfikacji.

Zmiana Oto, co prawdopodobnie zrobię:

Wykrywanie kodowania jest trudne.

mb_detect_encoding działa na zasadzie zgadywania spośród wielu kandydatów, których mijasz. W niektórych kodowaniach niektóre sekwencje bajtów są nieprawidłowe, więc może rozróżniać różnych kandydatów. Niestety, istnieje wiele kodowań, w których te same bajty są prawidłowe (ale różne). W takich przypadkach nie jest możliwe określenie kodowania; Możesz zaimplementować własną logikę, aby zgadywać w takich przypadkach. Na przykład dane pochodzące z japońskiej witryny prawdopodobnie będą zakodowane w języku japońskim.

Chociaż masz do czynienia tylko z językami zachodnioeuropejskimi, rozważ trzy główne kodowania: utf-8 , iso-8859-1 i cp-1252 . Ponieważ są one ustawieniami domyślnymi dla wielu platform, mogą być również błędnie zgłaszane. Na przykład. jeśli ludzie używają różnych kodowań, prawdopodobnie będą o tym szczerzy, ponieważ w przeciwnym razie ich oprogramowanie będzie się bardzo często łamać. Tak więc dobrą strategią jest zaufanie dostawcy, chyba że kodowanie jest zadeklarowane jako jedno z tych trzech. Nadal musisz podwoić, że jest to naprawdę ważne, używając mb_check_encoding (zauważ, że ważne to nie to samo, co bycie - to samo wejście może być ważne dla wielu kodowań). Jeśli jest to jeden z nich, możesz użyć mb_detect_encoding, aby je rozróżnić. Na szczęście jest to dość deterministyczne; Wystarczy użyć prawidłowej sekwencji wykrywania, czyli UTF-8,ISO-8859-1,WINDOWS-1252 .

Po znalezieniu kodowania musisz przekonwertować je na jego wewnętrzną reprezentację (utf-8 jest jedynym kodowaniem mądry wybór). Funkcja utf8_encode konwertuje iso-8859-1 na utf-8 , więc może być używana tylko dla tego konkretnego typu danych wejściowych. W przypadku innych kodowań użyj mb_convert_encoding .

Ta ściągawka zawiera kilka typowych zastrzeżeń związanych z obsługą UTF-8 przez PHP: http://developer.loftdigital.com/blog/php-utf-8-cheatsheet

Przydatna może być również ta funkcja, która wykrywa znaki wielobajtowe w łańcuchu ():

Funkcja wykryjUTF8($string) ( return preg_match("%(?: [\xC2-\xDF][\x80-\xBF] # 2-bajtowy nie-overlong |\xE0[\xA0-\xBF][\x80- \xBF] # z wyłączeniem overlongów |[\xE1-\xEC\xEE\xEF][\x80-\xBF](2) # prosty 3-bajtowy |\xED[\x80-\x9F][\x80-\xBF] # z wyłączeniem surogatów |\xF0[\x90-\xBF][\x80-\xBF](2) # płaszczyzny 1-3 |[\xF1-\xF3][\x80-\xBF](3) # płaszczyzny 4- 15 |\xF4[\x80-\x8F][\x80-\xBF](2) # płaszczyzna 16)+%xs", $string); )

Uwaga, powiedziałeś, że „ß” powinno pojawić się w Twojej bazie danych jako „Ÿ”.

To prawdopodobnie dlatego, że używasz bazy danych z kodowaniem znaków latin1, a może połączenie php-mysql jest źle skonfigurowane, php myśli, że twój mysql jest ustawiony na używanie utf-8, więc wysyła dane jako utf8, ale twój mysql wierzy, że php wysyła dane zakodowane jako iso-8859-1, więc może spróbować ponownie zakodować wysłane dane jako utf-8, powodując takie problemy.

Spójrz na to, może ci pomóc: http://php.net/manual/en/function.mysql-set-charset.php

Musisz sprawdzić kodowanie na wejściu, ponieważ odpowiedzi mogą być zakodowane z różnymi kodowaniami.
Wymuszam wysyłanie całej zawartości w UTF-8, wykonując wykrywanie i tłumaczenie za pomocą następującej funkcji:

Funkcja fixRequestCharset() ( $ref = array(&$_GET, &$_POST, &$_REQUEST); foreach ($ref as &$var) ( foreach ($var as $key => $val) ( $encoding = mb_detect_encoding ($var[$key ], mb_detect_order(), true); if (!$encoding) kontynuuj; if (strcasecmp($encoding, "UTF-8") != 0) ( $encoding = iconv($encoding, " UTF-8", $var[ $key ]); if ($encoding === false) kontynuuj; $var[ $key] = $encoding; ) ) ) )

Ta procedura zmieni wszystko Zmienne PHP, które pochodzą ze zdalnego hosta w UTF-8.
Lub zignoruj ​​​​wartość, jeśli nie można wykryć lub przekonwertować kodowania.
Możesz dostosować go do swoich potrzeb.
Po prostu wywołaj to przed użyciem zmiennych.

Twoje kodowanie wygląda na zakodowane w UTF-8 dwa razy; tj. z innego kodowania do UTF-8 i ponownie do UTF-8. Tak jakbyś miał iso-8859-1 przekonwertowany z iso-8859-1 na utf-8 i przeanalizowany Nowa linia jako iso-8859-1 dla kolejnej konwersji do UTF-8.

Oto pseudo kod tego, co zrobiłeś:

$inputstring = getFromUser(); $utf8string = iconv($bieżące_kodowanie, "utf-8", $ciąg wejściowy); $wadliwy ciąg = iconv($bieżące_kodowanie, "utf-8", $utf8string);

Musisz spróbować:

  • wykryj kodowanie za pomocą mb_detect_encoding() lub cokolwiek chcesz użyć
  • jeśli UTF-8, przekonwertuj na iso-8859-1 i powtórz krok 1
  • w końcu przekonwertuj z powrotem na UTF-8

Zakłada się, że użyłeś iso-8859-1 w konwersji „średniej”. Jeśli korzystałeś z Windows-1252, przekonwertuj na Windows-1252 (latin1). Oryginalne kodowanie źródłowe nie jest ważne; ten, którego użyłeś w błędnej, drugiej konwersji.

To jest moje przypuszczenie co do tego, co się stało; możesz zrobić trochę więcej, aby uzyskać cztery bajty zamiast jednego rozszerzonego bajtu ASCII.

Niemiecki używa również iso-8859-2 i windows-1250 (latin2).

Rozwój kodowania znaków kanału RSS wygląda na skomplikowany. Nawet normalne strony internetowe często pomijają lub kłamią na temat swojego kodowania.

Możesz więc spróbować użyć poprawnego sposobu wykrywania kodowania, a następnie wrócić do jakiejś formy automatycznego wykrywania (zgadywania).

Wiem, że to stare pytanie, ale wierzę, że użyteczna odpowiedź nigdy nie boli. Mam problem z kodowaniem między aplikacjami komputerowymi, zmiennymi SQLite i GET/POST. Część z nich będzie w UTF-8, część w ASCII, aw zasadzie rzeczy będą się mylić, gdy w grę wchodzą obce znaki.

Oto moje rozwiązanie. Spłaszcza twój GET/POST/REQUEST (przegapiłem ciasteczka, ale możesz je dodać w razie potrzeby) przy każdym ładowaniu strony przed przetworzeniem. Działa dobrze w nagłówku. PHP wyświetli ostrzeżenia, jeśli nie może automatycznie określić kodowania źródłowego, więc te ostrzeżenia są pomijane za pomocą @.

//Konwertuj wszystko w naszych vars na UTF-8, aby dobrze bawić się z bazą danych... //Użyj tutaj automatycznego wykrywania, aby pomóc nam uniknąć podwójnego kodowania... //Pomiń możliwe ostrzeżenia za pomocą @, gdy kodowanie nie może zostać wykryte try ( $process = array(&$_GET, &$_POST, &$_REQUEST); while (list($key, $val) = each($process)) ( foreach ($val as $k => $v) ( unset($process[$key][$k]); if (is_array($v)) ( $process[$key][@mb_convert_encoding($k,"UTF-8","auto")] = $ v; $process = &$process[$key][@mb_convert_encoding($k,"UTF-8","auto"]); ) else ( $process[$key][@mb_convert_encoding($k,"UTF- 8","auto")] = @mb_convert_encoding($v,"UTF-8","auto"); ) ) ) unset($proces); ) catch(Exception $ex)()

Sprawdzałem rozwiązania do kodowania z AGES i ta strona to prawdopodobnie koniec lat poszukiwań! Przetestowałem niektóre z sugestii, o których wspomniałeś, i oto moje notatki:

To jest mój ciąg testowy:

to jest ciąg "wròng wrìtten", którego nie użyłem specjalne dostosowanie chars, aby je zobaczyć, nawrócone przez funkcję!! Więc co to jest!

Czcionka mojej strony to UTF-8

Jeśli zrobię INSERT w ten sposób, mam kilka znaków w moim DB, które prawdopodobnie pochodzą z Marsa ... więc muszę je przekonwertować na „rozsądny” UTF-8. Próbowałem utf8_encode(), ale wciąż obce postacie atakowały moją bazę danych ...

Próbowałem więc użyć funkcji forceUTF8 opublikowanej pod numerem 8, ale w bazie danych zapisany ciąg znaków wygląda tak:

it's "wròng wrìtten" string bùt I nèed to pù „sà ²me” specjalne chà rs zobaczyć thèm, konwertowane przez fùnctìon!! Więc co to jest!

Tak więc, zbierając więcej informacji na tej stronie i łącząc je z innymi informacjami na innych stronach, rozwiązałem problem za pomocą tego rozwiązania:

$finallyIDidIt = mb_convert_encoding($string, mysql_client_encoding($resourceID), mb_detect_encoding($string));

Teraz w mojej bazie danych mam ciąg z poprawnym kodowaniem.

Notatka: Pamiętaj tylko, aby zająć się funkcją mysql_client_encoding! Musisz być połączony z bazą danych, ponieważ ta funkcja wymaga identyfikatora zasobu jako parametru.

Ale ok, po prostu robię to przekodowanie przed moim INSERT, więc nie stanowi to dla mnie problemu.

Mam nadzieję, że to pomoże komuś takiemu jak ta strona pomogła mi!

Dziękuję wszystkim!

Interesującą rzeczą w mb_detect_encoding i mb_convert_encoding jest to, że sugerowana kolejność kodowania ma znaczenie:

// $input to tak naprawdę UTF-8 mb_detect_encoding($input, "UTF-8", "ISO-8859-9, UTF-8"); // ISO-8859-9 (ŹLE!) mb_detect_encoding($input, "UTF-8", "UTF-8, ISO-8859-9"); // UTF-8 (OK)

Możesz więc użyć określonej kolejności przy określaniu oczekiwanych kodowań. Należy jednak pamiętać, że nie jest to wiarygodne.

echo mb_detect_encoding($str, "auto");

echo mb_detect_encoding($str, "UTF-8, ASCII, ISO-8859-1");

Naprawdę nie wiem, jakie są wyniki, ale sugeruję, abyś po prostu wziął kilka swoich kanałów z różnymi kodowaniami i spróbował, czy mb_detect_encoding działa, czy nie.

Aktualizacja
auto to skrót od „ASCII, JIS, UTF-8, EUC-JP, SJIS”. zwraca wykryte kodowanie, którego można użyć do konwersji ciągu na utf-8 za pomocą iconv .

Nie testowałem tego, więc nie ma gwarancji. a może jest prostszy sposób.

Ta wersja jest przeznaczona dla języka niemieckiego, ale można modyfikować $CHARSETS i $TESTCHARS

Klasa CharsetDetector ( private static $CHARSETS = array("ISO_8859-1", "ISO_8859-15", "CP850"); private static $TESTCHARS = array("€", "ä", "Ą, "ö", "Ö", "ü", "Ü", "ß"); publiczna funkcja statyczna convert($string) ( return self::__iconv($string, self::getCharset($string)); ) publiczna funkcja statyczna getCharset ($string) ( $normalized = self::__normalize($string); if(!strlen($normalized))return "UTF-8"; $best = "UTF-8"; $charcountbest = 0; foreach (self ::$CHARSETS jako $charset) ( $str = self::__iconv($normalized, $charset); $charcount = 0; $stop = mb_strlen($str, "UTF-8"); for($idx = 0 ;$idx< $stop; $idx++) { $char = mb_substr($str, $idx, 1, "UTF-8"); foreach (self::$TESTCHARS as $testchar) { if($char == $testchar) { $charcount++; break; } } } if($charcount>$charcountbest) ( $charcountbest=$charcount; $best=$charset; ) //echo $text."
"; ) return $best; ) prywatna funkcja statyczna __normalize($str) ( $len = strlen($str); $ret = ""; for($i = 0; $i< $len; $i++){ $c = ord($str[$i]); if ($c >128) ( if (($c > 247)) $ret .=$str[$i]; elseif ($c > 239) $bytes = 4; elseif ($c > 223) $bytes = 3; elseif ($ c > 191) $bajtów = 2, w przeciwnym razie $ret.=$str[$i], if (($i + $bytes) > $len) $ret.=$str[$i]; $ret2=$str [$i]; while ($bajty > 1) ( $i++; $b = ord($str[$i]); if ($b< 128 || $b >191) ($ret .=$ret2; $ret2=""; $i+=$bytes-1;$bytes=1; break;) else $ret2.=$str[$i]; $bajty--; ) ) ) zwróć $ret; ) prywatna funkcja statyczna __iconv($string, $charset) ( return iconv ($charset, "UTF-8" , $string); ) )

Po posortowaniu skryptów php nie zapomnij powiedzieć mysql, jakie kodowanie przekazujesz i chciałbyś je uzyskać.

Przykład: ustawienie zestawu znaków na utf8

Przekazywanie danych utf8 do tabeli latin1 w sesji we/wy latin1 daje te paskudne ptaszki. Widzę to codziennie w sklepach stacjonarnych. W tę iz powrotem może wydawać się słuszne. Ale phpmyadmin pokaże prawdę. Poinformowanie mysql, jakie kodowanie przekazujesz, poradzi sobie dane mysql dla Ciebie.

Jak odzyskać istniejące zaszyfrowane dane mysql to kolejna kwestia do omówienia. :)