2008-12-28 2 views
15

Chcę uzyskać punkty kodowe UCS-2 dla danego ciągu znaków UTF-8. Na przykład słowo "cześć" powinno brzmieć "0068 0065 006C 006C 006F". Należy pamiętać, że postacie mogą pochodzić z dowolnego języka, w tym ze złożonych skryptów, takich jak języki wschodnioazjatyckie.Jak uzyskać numer punktu kodu dla danej litery w ciągu utf-8?

Tak, problem sprowadza się do „przerobić dany znak jego UCS-2 punkt kodowy”

ale jak? Proszę, każda pomoc będzie bardzo ceniona, ponieważ bardzo się spieszę.

góry dzięki


Transkrypcja odpowiedzi pytający za wysłane jako odpowiedź

dzięki za odpowiedź, ale to musi być zrobione w PHP v 4 lub 5, ale nie 6.

Łańcuch będzie wprowadzany przez użytkownika z pola formularza.

chcę wdrożyć wersję PHP utf8to16 lub utf8decode jak

function get_ucs2_codepoint($char) 
{ 
    // calculation of ucs2 codepoint value and assign it to $hex_codepoint 
    return $hex_codepoint; 
} 

Czy możesz mi pomóc z PHP, lub można to zrobić z PHP w wersji wymienionych powyżej?

Jeszcze raz dziękuję.

+1

który język programowania? –

Odpowiedz

7

Scott Reynen napisał funkcję convert UTF-8 into Unicode. Znalazłem to patrząc na PHP documentation.

function utf8_to_unicode($str) { 

    $unicode = array();   
    $values = array(); 
    $lookingFor = 1; 

    for ($i = 0; $i < strlen($str); $i++) { 
     $thisValue = ord($str[ $i ]); 
    if ($thisValue < ord('A')) { 
     // exclude 0-9 
     if ($thisValue >= ord('0') && $thisValue <= ord('9')) { 
      // number 
      $unicode[] = chr($thisValue); 
     } 
     else { 
      $unicode[] = '%'.dechex($thisValue); 
     } 
    } else { 
      if ($thisValue < 128) 
     $unicode[] = $str[ $i ]; 
      else { 
       if (count($values) == 0) $lookingFor = ($thisValue < 224) ? 2 : 3;     
       $values[] = $thisValue;     
       if (count($values) == $lookingFor) { 
        $number = ($lookingFor == 3) ? 
         (($values[0] % 16) * 4096) + (($values[1] % 64) * 64) + ($values[2] % 64): 
         (($values[0] % 32) * 64) + ($values[1] % 64); 
      $number = dechex($number); 
      $unicode[] = (strlen($number)==3)?"%u0".$number:"%u".$number; 
        $values = array(); 
        $lookingFor = 1; 
      } // if 
     } // if 
    } 
    } // for 
    return implode("",$unicode); 

} // utf8_to_unicode 
+13

Napisałem tę funkcję. Oryginał jest tutaj, wraz z kilkoma innymi użytecznymi funkcjami: http://randomchaos.com/documents/?source=php_and_unicode –

+1

@Reynen, Mały świat, co? Zmieniłem swój post, by dać ci kredyt i reklamę. ; D – strager

+2

Proszę, och, proszę, nazwij to 'utf8_to_utf16'. Oba są "Unicode" w sposób, w jaki oba są reprezentacje dla punktów kodowych Unicode. – DarkDust

11

Użyj istniejącego narzędzia, takiego jak iconv lub dowolne biblioteki, które są dostarczane z językiem, którego używasz.

Jeśli kładziesz nacisk na rozwijaniu własnego rozwiązania, przeczytaj artykuł w formacie UTF-8. Zasadniczo każdy punkt kodowy jest przechowywany jako 1-4 bajty, w zależności od wartości punktu kodowego. Te zakresy są następujące:

  • U + 0000 - U + 007F: 1 bajt: 0xxxxxxx
  • U + 0080 - U + 07FF: 2 bajtów: 110xxxxx 10xxxxxx
  • U + 0800 - U + FFFF : 3 bajtów: 1110xxxx 10xxxxxx 10xxxxxx
  • U + 10000 - U + 10FFFF 4 bajty: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

gdzie każdy x jest nieco danych. W ten sposób można określić, ile bajtów komponuje każdy punkt kodowy, patrząc na pierwszy bajt: jeśli zaczyna się od 0, jest to znak 1-bajtowy. Jeśli zaczyna się od 110, jest to znak 2-bajtowy. Jeśli zaczyna się od 1110, jest to znak 3-bajtowy. Jeśli zaczyna się od 11110, jest to 4-bajtowa postać. Jeśli zaczyna się od 10, jest to nie-początkowy bajt znaku wielobajtowego. Jeśli zaczyna się od 11111, jest to nieprawidłowy znak.

Po ustaleniu, ile bajtów znajduje się w znaku, jest to po prostu kwestia bitowego twidowania. Zwróć też uwagę, że UCS-2 nie może reprezentować znaków powyżej U + FFFF.

Ponieważ nie określił język, oto niektóre przykładowy kod C (sprawdzanie błędów pominięta):

wchar_t utf8_char_to_ucs2(const unsigned char *utf8) 
{ 
    if(!(utf8[0] & 0x80))  // 0xxxxxxx 
    return (wchar_t)utf8[0]; 
    else if((utf8[0] & 0xE0) == 0xC0) // 110xxxxx 
    return (wchar_t)(((utf8[0] & 0x1F) << 6) | (utf8[1] & 0x3F)); 
    else if((utf8[0] & 0xF0) == 0xE0) // 1110xxxx 
    return (wchar_t)(((utf8[0] & 0x0F) << 12) | ((utf8[1] & 0x3F) << 6) | (utf8[2] & 0x3F)); 
    else 
    return ERROR; // uh-oh, UCS-2 can't handle code points this high 
} 
4

jestem rozbawiony, bo po prostu dał ten problem dla studentów na egzaminie końcowym.Oto szkic UTF-8:

hex   binary     UTF-8 binary 
0000-007F 00000000 0abcdefg => 0abcdefg 
0080-07FF 00000abc defghijk => 110abcde 10fghijk 
0800-FFFF abcdefgh ijklmnop => 1110abcd 10efghij 10klmnop 

A oto niektóre kod C99:

static void check(char c) { 
    if ((c & 0xc0) != 0xc0) RAISE(Bad_UTF8); 
} 

uint16_t Utf8_decode(char **p) { // return code point and advance *p 
    char *s = *p; 
    if ((s[0] & 0x80) == 0) { 
    (*p)++; 
    return s[0]; 
    } else if ((s[0] & 0x40) == 0) { 
    RAISE (Bad_UTF8); 
    return ~0; // prevent compiler warning 
    } else if ((s[0] & 0x20) == 0) { 
    if ((s[0] & 0xf0) != 0xe0) RAISE (Bad_UTF8); 
    check(s[1]); check(s[2]); 
    (*p) += 3; 
    return ((s[0] & 0x0f) << 12) 
     + ((s[1] & 0x3f) << 6) 
     + ((s[2] & 0x3f)); 
    } else { 
    check(s[1]); 
    (*p) += 2; 
    return ((s[0] & 0x1f) << 6) 
     + ((s[1] & 0x3f)); 
    } 
}  
+0

Ups, przepraszam za zmarnowanie twojego czasu z kodem C. Ale mam nadzieję, że ten diagram jest pomocny. –

+0

Czy funkcja check() nie jest zepsuta? Nie powinno to być testowanie: if ((c & 0xC0)! = 0x80)? Co to jest makro RAISE? –

+1

Dzięki za naprawienie błędu w funkcji check(). Odpowiada mi za pisanie kodu w noc przed finałem. RAISE pochodzi z http://www.cs.princeton.edu/software/cii/. –

4

kod PHP (który zakłada ważny UTF-8, nie ma wyboru dla nieważnej UTF-8):

function ord_utf8($c) { 
    $b0 = ord($c[0]); 
    if ($b0 < 0x10) { 
     return $b0; 
     } 
    $b1 = ord($c[1]); 
    if ($b0 < 0xE0) { 
     return (($b0 & 0x1F) << 6) + ($b1 & 0x3F); 
     } 
    return (($b0 & 0x0F) << 12) + (($b1 & 0x3F) << 6) + (ord($c[2]) & 0x3F); 
    } 
+0

Bardzo ładnie, z tym wyjątkiem, że zawiedzie, gdy znak jest już ASCII. 0x10 powinno być 0x80, aby uchwycić pełny zakres ASCII. – TextGeek