Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Problem z konwersją nieznanych kodowań na UTF8 w bazie SQLITE
Forum PHP.pl > Forum > PHP
nowy_pehapowiec
Dostałem bazę danych w której dane były wpisane w różnych kodowaniach. Część w językach europejskich a cześć w azjatyckich, przy czym teksty powinny zawierać tylko znaki łacińskie. Teksty z bazy są wyświetlane przy użyciu kodowania utf8 ale na niektórych komputerach w miejscach spacji albo przecinków pojawiają się krzaki. Czyli muszę teraz przekonwertować wszystkie wpisy w bazie na utf8. Ale nie znam kodowań źródłowych, część jest w UTF8, cześć w GB2312, część w różnych wersjach ISO (np iso-8859-1).
W dodatku bazą jest SQLITE, która, ponoć domyślnie używa UTF8.

Jak zamienić to kodowanie nie uszkadzając tekstów? Przejrzałem funkcje z pakietu multibyte ale nie działają w 100% dobrze. Jakieś pomysły?


pozdrawiam
wookieb
Pokaż jak używałeś funkcji z multibyte.
Co prawda jeszcze nigdy mnie nie zawiodły ale warto sprawdzić czy użyłeś ich poprawnie.
nowy_pehapowiec
Najpierw sprawdziłem jakie jest kodowanie:
echo mb_detect_encoding( $a ) //dostałem w wyniku UTF-8 a to nie prawda.
potem mimo wszystko spróbowałem konwersji:
$a = mb_convert_encoding( $a );
i zapisu do pliku
$p = fopen( 'p.txt', 'a+' );
fwrite( $p, $a );

i dostałem efekcie krzaki w niektórych miejscach. Czyli nic się nie zmieniło.

Coś zrobiłem źle? Co takiego, czemu to nie działa?
wookieb
Jak używasz mb_detect_encoding to używaj jej z drugim parametrem np
  1. echo mb_detect_encoding($tekst, 'utf-8, jakiesChinskie, itd')

Po tej operacji do przekodowania ciągu możesz użyć iconv.
nowy_pehapowiec
wookieb ale problem w tym, że ja nie wiem jakie pola w bazie mają jakie kodowanie.

Nie rozumiem jeszcze po co mam używać iconv? Czy samo zastosowanie mb_convert_encoding nie wystarczy? Proszę czy mógłbyś mi wytłumaczyć jak po kolei zrobić tą konwersje? I jeszcze jedno jak wyciąć ze $stringu znaki spoza kodowania utf-8? Czyli co zrobić, żeby w konwertowanym stringu zostawić tylko znaki które są w utf-8 i nie potrzeba instalować na kompie kodowania chińskiego?


Sprawdziłem jeszcze, że funkcja
echo iconv_get_encoding ( $a ) nic nie zwraca.
A wywołanie
$a = iconv('gb2312','UTF-8', $a);
powoduje błąd
Detected an illegal character in input string

Co robić?
wookieb
Trochę pomyślunku.
Przecież napisałeś, że znasz przynajmniej 3 kodowania znaków a to JUŻ DUŻO INFORMACJI.
Podałem ci jak masz użyć mb_detect_encoding, której podajesz jaki drugi argument KODOWANIA JAKIE ZNASZ (przykład masz nawet w manualu).
Przeleć po tych ciągach i zobacz dla których funkcja mb_detect_encoding nie jest w stanie wykryć kodowania, olejesz je i potem pokombinujesz jakie to może być kodowanie (np pozgadywać).
Dla tych których wykryło odpowiednie kodowanie użyć iconv
  1. $ciag = iconv($WYKRYTE_KODOWANIE_PRZEZ_MB, 'utf-8', $ciag);

I zapisujesz ten ciąg w bazie. Pach masz kodowanie utf-8.
nowy_pehapowiec
mb nie wykrywa żadnego kodowania poprawnie. Kodowanie znam tylko na podstawie zgadywania ze zmian ustawień w FireFox. Jakieś sugestie? Np jak pozamieniać wybrany robaczek na znak z zestawu utf-8?
wookieb
Wrzuć tą tabelę do jakieś pliku i wystam. Mogę się założyć, że mb + iconv dadzą rade.
Tylko poprawnie wyeksportuje tabelę.
nowy_pehapowiec
Udało mi się ustalić, że w tekście są jednocześnie znaki UTF i inne. Dlatego, nie udaje się żadnym funkcjom ustalić kodowana - bo w jednym stringu jest ich kilka.

Co robić? Bardzo proszę o pomoc - sprawa jest dla mnie niezmiernie ważna. A coś czuje, że sam jej nie rozwiąże.


pozdrawiam






Po godzinach spędzonych na przeszukiwaniu neta obawiam się, że nie ma gotowego rozwiązania mojego problemu.

Ponieważ znaków niezgodnych z UTF8 nie jest dużo, więc będę zamieniać wartości bitów. W edytorze HEX sprawdzę, które bity odpowiadają za "złe" znaki, skopiuje ich wartości i przy pomocy zwykłego str_replace zamienię na takie wartości HEX jakie odpowiadają dobrym znakom. Czyli rzecz się sprowadza do zrobienia tabelki z odpowiednimi wartościami HEX. Jest jeden problem. Mogę coś przeoczyć. Dlatego potrzebuje listy wszystkich znaków z UTF-8 w zapisie HEX, żeby sprawdzić czy nic nie pomniałem. Czy ktoś wie gdzie znajdę taką listę? I dwa jak sprawdzić czy w stringu występują znaki inne niż w wybranej tablicy? Czyli jak sprawdzić czy string zawiera znaki z poza UTF-8, ewentualnie ASCII?

Pomoże ktoś?
Zyx
Istnieje możliwość takiego sprawdzenia. Sekwencje wielobajtowe w UTF-8 zostały tak dobrane, że kontrola błędu jest stosunkowo prosta bez żadnej dodatkowej tablicy znaków (btw. powodzenia z używaniem takowej - standard Unicode liczy miliony pozycji smile.gif). Będę podawać tutaj wartości bajtów zapisane binarnie, gdyż tam najlepiej to widać (x - dowolna wartość, koduje numer znaku).

0xxxxxxx - normalny znak ASCII z zakresu 0-127.
10xxxxxx - drugi lub każdy kolejny bajt "wyższych" znaków Unicode. Może być poprzedzony jedynie poprawną sekwencją startową UTF-8 lub innym 10xxxxxx. Jeśli jest przed nim coś innego, taki znak jest błędnie zakodowany.

Sekwencje startowe UTF-8 - kodują znaki od numerów 128 w górę.

110xxxxx - sekwencja dwubajtowa. Po niej musi następować JEDEN bajt 10xxxxxx.
1110xxxx - sekwencja trzybajtowa. Po niej muszą następować DWA bajty 10xxxxxx.
11110xxx - sekwencja czterobajtowa...
itd.

Sprawdzanie poprawności można wykonać przy pomocy prostego automatu skończonego (de facto wyrażenie regularne by wystarczyło, ale jak masz jeszcze po drodze poprawiać źle zakodowane znaki, to musisz własny zaklepać). Potrzebna Ci jest pętla, która jedzie po ciągu od początku do końca oraz jedna zmienna w której masz zapisany aktualny stan. Na Wikipedii możesz sobie doczytać, jak taki automat działa, bo tu szkoda na to miejsca. Opiszę jedynie tabelę przejść w postaci

(stan, aktualny znak) -> nowy stan

(domyślny, 0xxxxxxx) -> domyślny
(domyślny, 10xxxxxx) -> błąd_1
(domyślny, 110xxxxx) -> 2b_1
(domyślny, 1110xxxx) -> 3b_1
(domyślny, 11110xxx) -> 4b_1
(domyślny, 111110xx) -> 5b_1
(domyślny, 1111110x) -> 6b_1

Sprawdzanie sekwencji dwubajtowej:

(2b_1, 10xxxxxx) -> domyślny
(2b_1, xxxxxxxx) -> błąd_2

Sprawdzanie dla sekwencji trzybajtowej:

(3b_1, 10xxxxxx) -> 3b_2
(3b_1, xxxxxxxx) -> błąd_2
(3b_2, 10xxxxxx) -> domyślny
(3b_2, xxxxxxxx) -> błąd_2

Analogicznie konstruujesz stany dla sekwencji 4, 5 i 6-bajtowych. Jak dojdziesz do końca, to jest poprawne.

Taki automat sprawdza jedynie poprawność, ty go rozbudujesz o dodatkowy kod:

1. Przy przejściu do stanu "domyślny" przepisujesz znak do bufora $poprawny zawierającego poprawiony ciąg.
2. Przy napotkaniu sekwencji startowej UTF-8, czyścisz bufor $znakUTF i wprowadzasz tam odczytany bajt.
3. W stanach typu 3b_2, 2b_1 itd. czyli tych, co sprawdzają kolejne bajty znaku UTF-8 dodajesz je do bufora $znakUTF. Jeśli to był ostatni bajt danej sekwencji, dodajesz $znakUTF do bufora $poprawny.
4. Przy wejściu do stanu błąd_1 masz symbol z jakiegoś kodowania jednobajtowego (np. ISO-8859-2), który konwertujesz na odpowiadającą mu kilkubajtową sekwencję UTF-8. Sekwencję tę dodajesz do bufora $poprawny i natychmiast przechodzisz do stanu domyślny tak, żeby analiza mogła od kolejnego znaku trwać dalej.
5. Przy wejściu do stanu błąd_2 masz sytuację, gdy sekwencja UTF-8 rozpoczęła się poprawnie, ale w którymś miejscu pojawił się błąd. Wtedy bierzesz każdy bajt ze $znakUTF, traktujesz go jako jakiś symbol w kodowaniu jednobajtowym, konwertujesz na odpowiadającą mu sekwencję UTF-8 i dodajesz ją do bufora $poprawny. Po zakończeniu od razu zmieniasz stan na domyślny, by od następnego znaku rozpocząć dalej normalną analizę.

Oczywiście nic nie zagwarantuje Ci 100% poprawnej konwersji ciągu, w którym masz wymieszany UTF-8 i kodowania jednobajtowe. Jeśli ciąg znaków z przedziału 128-255 przypadkiem będzie tworzyć poprawną sekwencję znaku UTF-8, każdy algorytm, który nie próbuje zrozumieć czytanego tekstu nie zasygnalizuje tego jako błąd. Sytuacja taka jest mało prawdopodobna, ale możliwa.
nowy_pehapowiec
Zyx Twoje rozwiązanie jest super, ale ma dużą wadę, jego napisanie jest troszkę trudne i bardzo czasochłonne.

Mi udało się wymyślić coś innego, ale nie wiem czy będzie dobre.

1 Wklejam tablice wartości HEX dla kodowania UTF-8 (http://www.utf8-chartable.de/). Tekst jest po angielsku, więc powinno wystarczyć, ale o tym potem.
2 Wszystkie pola z tekstem sklejam w jedną zmienną.
3 Następnie funkcją str_split rozdzielam znaki w tekście.
4 Każdy znak zamieniam funkcją bin2hex na wartości HEX i zapasuje do tablicy
5 Porównuje tablice z hexami z UTF-8 i z hexami z tekstu. Te które są w tekście a których nie ma w UTF zapisuje do kolejnej tablicy.
6 Sprawdzam jaka sekwencja hex w UTF-8 jest odpowiednikiem wybranych znaków.
7 Zamieniam w każdym polu "złe"hexy na "dobre"

Czy to będzie działać? Jest w tym może jakiś błąd?


pozdro
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.