Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [PHP][utf8]Przechodzenie po kolejnych znakach
Forum PHP.pl > Forum > Przedszkole
ocochodzi
Głupia sprawa. Prosta rzecz a nie wiem jak ją rozwiazać...

String w kodowaniu 8 bitowym można indeksować jak tablicę. Nie jest problemem odwoływanie się do n-tego znaku w stringu. A jak się do niego odwołać w stringu zakodowanym w utf-8? Przypuszczam, że prostego odwołania do n-tego znaku nie ma, bo długości kodów znaków są zmienne w utf8. Ale mniejsza z tym. Wystarczy mi jakiś sposób na pobieranie kolejnych znaków ze stringa zakodowanego w utf8.

Pogooglałem i jak na złość znalazłem temat na stronie Włodzimierza Gajdy. Bardzo fajna strona, kiedyś tam sobie sporo czytałem, ale okazało się, że zmieniła formułę na bardziej komercyjną...

Będę wdzięczny za naprowadzenie. Z tego co wyczytałem o utf8, to napisanie takiej procedurki w C byłoby raczej proste. No, ale w PHP takich rzeczy raczej sie nie koduje, a ja nie potrafię znaleźć gotowca sad.gif
-gox-
w zwiazku z tym ze w utf-8 poslkie krzaki zajmuja 16 bajtow, mozna sie do nich dobierac w nastepujacy sposob:

  1. <?php
  2. $string = 'abcęóżźńąłxyz';
  3. echo $string[5];
  4. echo $string[6];
  5. // output: ó
  6. ?>
-gox-
wiec mozna napisac samemu funkcje, ktora bedzie skakac jak substr, a kiedy znajzie polski krzak, zwroci 16 bajtowy string z polska litera, zamiast 8 bajtowego
ocochodzi
Dziękuję za odpowiedź ale:

Cytat(-gox- @ 31.10.2008, 21:50:37 ) *


Jak (mb)substringiem wyciągać podciągi to wiem, ale przypuszczam, ze w utf8 ta procedurka musi przejść przez cały string aż do wycinanego elementu (aby wiedzieć gdzie są znaki o odpowiednim indeksie). Zatem jeśli chce sobie przeparsować dużego stringa to będę miał złożoność kwadratową a nie liniową. tzn. przejście po 1 znaku, po 2, po 3...po N, suma 1+2+3...=O(N^2) (za każdym razem mb-substr szuka od początku). Zrobię sobie testy wydajnościowe żeby się upewnić. Na razie to taka spekulacja w oparciu o zastanawianie się skąd mb_substr ma wiedzieć gdzie zaczyna się np. 345 znak.

Polskie znaki...Ale ja chce mieć unicode. Gdyby to była sprawa polskich znaków, to bym sobie przekonwertował na iso 8859-2 i na tym parsował. Tak sobie teraz myślę, że może są jeszcze kodowania unicode oparte o kody stałej długości i nasz wspaniały php umożliwia na nie konwersje...
zegarek84
a czemu do np. piątego znaku nie odwołasz po prostu mb_substr($string,4,1,'utf-8') questionmark.gif
po prostu jako ostatni parametr podajesz kodowanie....:
string mb_substr ( string $str , int $start [, int $length [, string $encoding ]] )
ocochodzi
Cytat(zegarek84 @ 1.11.2008, 16:42:04 ) *
a czemu do np. piątego znaku nie odwołasz po prostu mb_substr($string,4,1,'utf-8') questionmark.gif


z powodów jakie wymieniłem wyżej. Chce przeparsować string zakodowany w utf8. Nie chcę jakiegokolwiek rozwiązania, tylko rozwiazanie estetyczne z punktu widzenia optymalności. Rozwiązanie nazujące na mb_substr wygląda tak:

  1. <?php
  2. for($i=0; $i<mb_strlen($napis); $i++)
  3.    $x = mb_substr($napis,$i,1);
  4. ?>


Podejrzewam, że mb_substr musi przejść po całym $napis aż do pozycji $i. Zatem powyższe rozwiązanie ma złożoność O(N^2) a nie O(N) - a tego drugiego oczekujemy po przetwarzaniu tekstu znak po znaku. Zrobiłem eksperyment żeby potwierdzić albo rozwiać podejrzenia. Odpalam skrypt testowy, który przechodzi kolejno po napisach zakodowanych w utf8 odpowiednio długości: 1000 znakow, 2000, 3000,...aż do stringów 50000 znakowych. Sprawdzam zależność czasu wykonywania od długości stringa.

  1. <?php
  2. mb_internal_encoding("UTF-8");
  3.  
  4. // przygotowanie napisu o długości 1000*50 = 50 0000 znaków
  5. $napis = '';
  6. for($i = 0; $i<1000; ++$i)
  7.    $napis .= 'aaa中国6789łłłłłłłłłł*()&^%#@%!qwertyQWERTYóżźćXXłłż';
  8.    echo 'liczba znaków w stringu: ' . $rmax = mb_strlen($napis) . "<br>\n";
  9.    
  10. // przejście znak po znaku przez napisy o długości $p=1000,2000,3000,... < 50000 znaków
  11. for($p = 1000; $p < $rmax; $p += 1000)
  12. {
  13.    $czas_start = microtime(TRUE);
  14.    echo 'napis długości:' . $p;
  15.    for($iter = 1; $iter < $p; ++$iter)  // tu przechodzimy znak po znaku
  16.    {  
  17.        $x = mb_substr($napis,$iter,1);    
  18.    }
  19.    $czas_stop = microtime(TRUE);
  20.    $czas_wykonania[$p] = ($czas_stop - $czas_start);// / $iteracje;
  21.    echo ', czas wykonania: ' . $czas_wykonania[$p] . "<br>\n";
  22. }    
  23. echo 'KONIEC, teraz zapiszemy wynik do pliku CSV';
  24.  
  25. $p = fopen('wynik.txt','wb');
  26. if($p === FALSE)
  27.    throw new Exception('PLIK, PLIK!!!');
  28. // zapis na plik
  29. foreach($czas_wykonania as $dlugoscstr => $czaswyk)
  30. {
  31.    if(fputcsv($p, array($dlugoscstr,$czaswyk)) === FALSE)
  32.        throw new Exception('PLIK, PLIK!!!');
  33. }
  34. ?>


w wyniku otrzymałem:

liczba znaków w stringu: 50000
napis długości:1000, czas wykonania: 0.045909881591797
napis długości:2000, czas wykonania: 0.11165714263916
napis długości:3000, czas wykonania: 0.22915983200073
napis długości:4000, czas wykonania: 0.24982881546021
napis długości:5000, czas wykonania: 0.3840639591217
napis długości:6000, czas wykonania: 0.48809003829956
napis długości:7000, czas wykonania: 0.60801005363464
napis długości:8000, czas wykonania: 0.83047580718994
napis długości:9000, czas wykonania: 0.93301296234131
napis długości:10000, czas wykonania: 1.0732500553131
napis długości:11000, czas wykonania: 1.2416779994965
napis długości:12000, czas wykonania: 1.4608471393585
napis długości:13000, czas wykonania: 1.7372689247131
napis długości:14000, czas wykonania: 1.895831823349
napis długości:15000, czas wykonania: 2.1436331272125
napis długości:16000, czas wykonania: 2.385999917984
napis długości:17000, czas wykonania: 2.6672129631042
napis długości:18000, czas wykonania: 2.9463531970978
napis długości:19000, czas wykonania: 3.1730628013611
napis długości:20000, czas wykonania: 3.5410470962524
napis długości:21000, czas wykonania: 3.8248689174652
napis długości:22000, czas wykonania: 4.1826858520508
napis długości:23000, czas wykonania: 4.5365719795227
napis długości:24000, czas wykonania: 4.915678024292
napis długości:25000, czas wykonania: 5.2788889408112
napis długości:26000, czas wykonania: 5.6325259208679
napis długości:27000, czas wykonania: 6.0388510227203
napis długości:28000, czas wykonania: 6.462660074234
napis długości:29000, czas wykonania: 6.9221448898315
napis długości:30000, czas wykonania: 7.2982220649719
napis długości:31000, czas wykonania: 7.8054759502411
napis długości:32000, czas wykonania: 8.2694098949432
napis długości:33000, czas wykonania: 8.7420930862427
napis długości:34000, czas wykonania: 9.2250289916992
napis długości:35000, czas wykonania: 9.7165539264679
napis długości:36000, czas wykonania: 10.300148010254
napis długości:37000, czas wykonania: 10.80548620224
napis długości:38000, czas wykonania: 11.390262126923
napis długości:39000, czas wykonania: 11.927836179733
napis długości:40000, czas wykonania: 12.468158960342
napis długości:41000, czas wykonania: 13.08842086792
napis długości:42000, czas wykonania: 13.647618055344
napis długości:43000, czas wykonania: 14.36807012558
napis długości:44000, czas wykonania: 14.909945964813
napis długości:45000, czas wykonania: 15.589555025101
napis długości:46000, czas wykonania: 16.189097166061
napis długości:47000, czas wykonania: 17.128329992294
napis długości:48000, czas wykonania: 17.590538978577
napis długości:49000, czas wykonania: 19.319259881973
KONIEC, teraz zapiszemy wynik do pliku CSV

a po zrobieniu wykresu z pliku CSV:

http://img201.imageshack.us/my.php?image=zlozzc1.gif

czyli złożoność algorytmu z mb_substr traktowanym jako "nextchar" jest na oko kwadratowa. Potwierdzają się więc moje podejrzenia.
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.