Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> Problem z konwersją nieznanych kodowań na UTF8 w bazie SQLITE
nowy_pehapowiec
post
Post #1





Grupa: Zarejestrowani
Postów: 220
Pomógł: 0
Dołączył: 24.08.2009

Ostrzeżenie: (0%)
-----


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

Ten post edytował nowy_pehapowiec 18.11.2009, 13:21:53
Go to the top of the page
+Quote Post
wookieb
post
Post #2





Grupa: Moderatorzy
Postów: 8 989
Pomógł: 1550
Dołączył: 8.08.2008
Skąd: Słupsk/Gdańsk




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.


--------------------
Go to the top of the page
+Quote Post
nowy_pehapowiec
post
Post #3





Grupa: Zarejestrowani
Postów: 220
Pomógł: 0
Dołączył: 24.08.2009

Ostrzeżenie: (0%)
-----


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?
Go to the top of the page
+Quote Post
wookieb
post
Post #4





Grupa: Moderatorzy
Postów: 8 989
Pomógł: 1550
Dołączył: 8.08.2008
Skąd: Słupsk/Gdańsk




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.


--------------------
Go to the top of the page
+Quote Post
nowy_pehapowiec
post
Post #5





Grupa: Zarejestrowani
Postów: 220
Pomógł: 0
Dołączył: 24.08.2009

Ostrzeżenie: (0%)
-----


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ć?

Ten post edytował nowy_pehapowiec 19.11.2009, 11:29:20
Go to the top of the page
+Quote Post
wookieb
post
Post #6





Grupa: Moderatorzy
Postów: 8 989
Pomógł: 1550
Dołączył: 8.08.2008
Skąd: Słupsk/Gdańsk




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.


--------------------
Go to the top of the page
+Quote Post
nowy_pehapowiec
post
Post #7





Grupa: Zarejestrowani
Postów: 220
Pomógł: 0
Dołączył: 24.08.2009

Ostrzeżenie: (0%)
-----


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?
Go to the top of the page
+Quote Post
wookieb
post
Post #8





Grupa: Moderatorzy
Postów: 8 989
Pomógł: 1550
Dołączył: 8.08.2008
Skąd: Słupsk/Gdańsk




Wrzuć tą tabelę do jakieś pliku i wystam. Mogę się założyć, że mb + iconv dadzą rade.
Tylko poprawnie wyeksportuje tabelę.

Ten post edytował wookieb 19.11.2009, 14:05:01


--------------------
Go to the top of the page
+Quote Post
nowy_pehapowiec
post
Post #9





Grupa: Zarejestrowani
Postów: 220
Pomógł: 0
Dołączył: 24.08.2009

Ostrzeżenie: (0%)
-----


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ś?

Ten post edytował nowy_pehapowiec 20.11.2009, 12:57:04
Go to the top of the page
+Quote Post
Zyx
post
Post #10





Grupa: Zarejestrowani
Postów: 952
Pomógł: 154
Dołączył: 20.01.2007
Skąd: /dev/oracle

Ostrzeżenie: (0%)
-----


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.

Ten post edytował Zyx 25.11.2009, 10:31:30


--------------------
Specjalista ds. głupich i beznadziejnych, Zyx
Nowości wydawnicze: Open Power Collector 3.0.1.0 | Open Power Autoloader 3.0.3.0
Go to the top of the page
+Quote Post
nowy_pehapowiec
post
Post #11





Grupa: Zarejestrowani
Postów: 220
Pomógł: 0
Dołączył: 24.08.2009

Ostrzeżenie: (0%)
-----


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
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Aktualny czas: 20.08.2025 - 13:31