2015-06-02 17 views
6

Buduję API dla aplikacji mobilnej i wydaje mi się, że mam problem z policzeniem długości łańcucha zawierającego emotikony. Mój kod:PHP - długość łańcucha zawierającego emotikony/znaki specjalne

$str = "✌️ @mention"; 

printf("strlen: %d" . PHP_EOL, strlen($str)); 
printf("mb_strlen UTF-8: %d" . PHP_EOL, mb_strlen($str, "UTF-8")); 
printf("mb_strlen UTF-16: %d" . PHP_EOL, mb_strlen($str, "UTF-16")); 
printf("iconv UTF-16: %d" . PHP_EOL, iconv_strlen(iconv("UTF-8", "UTF-16", $str))); 
printf("iconv UTF-16: %d" . PHP_EOL, iconv_strlen(iconv("ISO-8859-1", "UTF-16", $str))); 

odpowiedź to:

strlen: 27 
mb_strlen UTF-8: 14 
mb_strlen UTF-16: 13 
iconv UTF-16: 14 
iconv UTF-16: 27 

jednak powinien dostać 17 jako wynik. Próbowaliśmy połączyć długość ciągu w telefonach z systemem iOS, Android i Windows, wszędzie jest ich 17. iOS (swift):

var str = "✌️ @mention" 
(str as NSString).length // 17 
count(str) // 13 
count(str.utf16) // 17 
count(str.utf8) // 27 

Musimy użyć NSString z powodu biblioteki. Potrzebuję tego, aby uzyskać początkową i końcową pozycję "@mention". Jeśli ciąg zawiera tylko tekst lub tylko emotikony, działa dobrze, więc prawdopodobnie występuje problem z mieszaną zawartością.

Co robię źle? Jakie inne informacje mogę wam zapewnić, abyście poszli we właściwym kierunku?

Dzięki!

+0

spróbuj mb_substr, mb_str długość może być opcja –

Odpowiedz

12

Twoje funkcje liczą różne rzeczy.

Graphemes:          ✌    ️      @  m  e  n  t  i  o  n 13 
         ----------- ----------- -------- --------------------- ------ ------ ------ ------ ------ ------ ------ ------ ------ 
Code points:   U+1F44D  U+1F3FF  U+270C  U+1F3FF  U+FE0F U+0020 U+0040 U+006D U+0065 U+006E U+0074 U+0069 U+006F U+006E 14 
UTF-16 code units:  D83D DC4D D83C DFFF  270C  D83C DFFF  FE0F  0020 0040 006D 0065 006E 0074 0069 006F 006E 17 
UTF-16-encoded bytes: 3D D8 4D DC 3C D8 FF DF 0C 27 3C D8 FF DF 0F FE 20 00 40 00 6D 00 65 00 6E 00 74 00 69 00 6F 00 6E 00 34 
UTF-8-encoded bytes: F0 9F 91 8D F0 9F 8F BF E2 9C 8C F0 9F 8F BF EF B8 8F 20  40  6D  65  6E  74  69  6F  6E 27 

Łańcuchy PHP są natywnie bajtami.

strlen() zlicza liczbę bajtów w ciągu: 27.

mb_strlen(..., 'utf-8') zlicza ilość punktów kodowych (znaków UNICODE) w ciągu gdy jego bajty są dekodowane do postaci przy użyciu kodowania UTF-8: 14.

(innym przykładem liczy są w dużej mierze bez znaczenia, ponieważ są one oparte na leczeniu ciąg wejściowy jako jednego kodowania gdy faktycznie zawiera dane w innym kodowaniu.)

NSStrings są natywnie liczony jako UTF-16 jednostek kodowych . Jest ich 17, a nie 14, ponieważ powyższy ciąg zawiera znaki takie jak , które nie mieszczą się w jednej jednostce kodowej UTF-16, więc muszą być zakodowane jako para zastępcza. Nie ma żadnych funkcji, które zliczyłyby napisy w jednostkach kodowych UTF-16 w PHP, ale ponieważ każda jednostka kodowa jest zakodowana do dwóch bajtów, można to przetworzyć na tyle łatwo, kodując do UTF-16 i dzieląc liczbę bajtów przez dwa:

strlen(iconv('utf-8', 'utf-16le', $str))/2 

(Uwaga: le przyrostek jest konieczne, aby iconv kodowanie do konkretnego bajt UTF-16, a nie zniweczyć liczyć wybierając jedną i dodawania BOM na początku łańcucha do powiedz, który z nich wybrał.)

+0

GREAT! dzięki :) działa! – gabo

+0

mówi 14, ale tylko 7 !! Twoja metoda nie działa. – Sibidharan

+1

@Sibidharan: co masz na myśli, "moja metoda"? Jaką formę liczenia używałeś i czego się spodziewałeś? Jak w powyższej tabeli, '' jest 7 kodami Unicode, 14 kodami UTF-16 lub 29 bajtami UTF-8. – bobince

4

Dołączyłem obrazek, aby zilustrować odpowiedź udzieloną przez @bobince.

Zasadniczo, wszystkie punkty kodowe, które nie są parami zastępczymi, kończą się jako dwa bajty w UTF-16, podczas gdy wszystkie pary kodów zastępczych kończą się czterema bajtami. Jeśli podzielimy to przez dwa, otrzymamy równoważną oczekiwaną wartość długości.

P.S.Proszę wybaczyć błąd w obrazie, gdzie mówi „punkty kodowe” i powinien powiedzieć „Kod Jednostki”

unicode breakdown