2015-06-02 40 views
6

Tôi đang tạo API cho ứng dụng dành cho thiết bị di động và dường như tôi gặp sự cố khi đếm chiều dài của chuỗi chứa biểu tượng cảm xúc. Mã của tôi:PHP - độ dài chuỗi chứa biểu tượng cảm xúc/ký tự đặc biệt

$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))); 

phản ứng của việc này là:

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

tuy nhiên tôi sẽ nhận được 17 như kết quả. Chúng tôi đã cố gắng giới hạn độ dài chuỗi trên iOS, android và cửa sổ điện thoại, đó là 17 ở khắp mọi nơi. Đoạn mã iOS (swift):

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

Chúng tôi cần sử dụng NSString vì thư viện. Tôi cần điều này để có được vị trí bắt đầu và kết thúc của "@mention". Nếu chuỗi chỉ chứa văn bản hoặc chỉ các biểu tượng cảm xúc, chuỗi đó hoạt động tốt nên có thể có một số vấn đề với nội dung hỗn hợp.

Tôi đang làm gì sai? Tôi có thể cung cấp thông tin nào khác cho bạn để giúp tôi đi đúng hướng?

Cảm ơn!

+0

hãy thử sử dụng mb_substr, chiều dài mb_str có thể là một lựa chọn –

Trả lời

12

Các chức năng của bạn đều đang đếm những thứ khác nhau.

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 

chuỗi PHP là natively byte.

strlen() đếm số byte trong một chuỗi: 27.

mb_strlen(..., 'utf-8') đếm số lượng các điểm mã (ký tự Unicode) trong một chuỗi khi byte của nó được giải mã để ký tự bằng cách sử dụng mã hóa UTF-8: 14.

(The đếm ví dụ khác là phần lớn là vô nghĩa khi chúng được dựa trên điều trị chuỗi đầu vào là một mã hóa khi thực sự nó chứa dữ liệu trong một bảng mã khác nhau.)

NSStrings được natively tính là-16 đơn vị mã UTF . Có 17, không phải 14, vì chuỗi ở trên chứa các ký tự như không phù hợp với một đơn vị mã UTF-16, do đó phải được mã hóa thành cặp thay thế. Không có bất kỳ hàm nào sẽ đếm chuỗi trong đơn vị mã UTF-16 trong PHP, nhưng vì mỗi đơn vị mã được mã hóa thành hai byte, bạn có thể làm việc dễ dàng bằng cách mã hóa thành UTF-16 và chia số byte theo hai:

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

(Lưu ý: le hậu tố là cần thiết để làm iconv mã hóa sang một endianness cụ thể của UTF-16, và không hôi lên tính bằng cách chọn một và thêm một BOM đến sự bắt đầu của chuỗi nói cái nào nó đã chọn.)

+0

GREAT! cảm ơn :) nó hoạt động! – gabo

+0

nói 14, nhưng chỉ 7 !! Phương pháp của bạn dường như không hoạt động. – Sibidharan

+1

@Sibidharan: ý của bạn là gì, “phương pháp của tôi”? Bạn đã sử dụng hình thức đếm nào và bạn mong đợi điều gì? Theo bảng trên, '' là 7 điểm mã Unicode, 14 đơn vị mã UTF-16, hoặc 29 byte UTF-8. – bobince

4

Tôi đã bao gồm một hình ảnh để giúp minh họa câu trả lời mà @bobince đưa ra.

Về cơ bản, tất cả các điểm mã không thay thế thay thế kết thúc dưới dạng hai byte trong UTF-16 trong khi tất cả các điểm mã thay thế kết thúc là bốn byte. Nếu chúng ta chia chúng cho hai, chúng ta sẽ nhận được giá trị độ dài mong đợi tương đương.

P.S.Hãy tha thứ cho những lỗi trong hình ảnh mà nó nói "điểm mã" và nên nói "đơn vị mã"

unicode breakdown

Các vấn đề liên quan