2015-06-03 18 views
5

Trích dẫn sự giúp đỡ Delphi XE8:Chiều dài() vs sizeof() trên Unicode Strings

Đối với single-byte và multibyte chuỗi, Chiều dài trả về số byte được sử dụng bởi các chuỗi. Ví dụ cho UTF-8:

Writeln(Length(Utf8String('1¢'))); // displays 3 

Đối với chuỗi Unicode (WideString), Độ dài trả về số byte chia cho hai.

này nảy sinh câu hỏi quan trọng:

  1. Tại sao sự khác biệt trong việc xử lý luôn luôn ở đó?
  2. Tại sao Length() không thực hiện những gì được mong đợi, chỉ trả về độ dài của tham số (như trong số lượng phần tử) thay vì cho kích thước tính theo byte trong một số trường hợp?
  3. Tại sao tiểu bang phân chia kết quả bằng 2 cho chuỗi Unicode (UTF-16)? AFAIK UTF-16 tối đa là 4 byte và do đó điều này sẽ cho kết quả không chính xác.
+0

thử 'LenInBytes: = Độ dài (UTF8Encode ('строка'))' hoặc 'var u8: UTF8String; u8: = 'строка'; I: = Length (u8) '- không có typecast –

+0

Tôi đã viết điều này bởi vì nó theo nghĩa đen được viết bằng tên của mã hóa bao nhiêu nó cần để mã hóa một nhân vật. Tôi chỉ nhầm lẫn với mã hóa khác. – ZzZombo

+0

Tôi đã hoàn nguyên tất cả các chỉnh sửa của bạn. Chủ yếu là bởi vì tôi không đặc biệt muốn tiếp tục cập nhật câu trả lời của mình cho phù hợp! ;-) Dù sao, tôi nghĩ rõ ràng là bạn đang ở trên đầu trang của điều này ngay bây giờ. Câu hỏi là một câu hỏi hay. Chúng ta không thể để nó như thế được. –

Trả lời

8

Length trả về số phần tử khi xem chuỗi là mảng.

  • Đối với chuỗi có loại phần tử 8 bit (ANSI, UTF-8) thì Length cho bạn số byte vì số byte giống với số lượng phần tử.
  • Đối với các chuỗi có phần tử 16 bit (UTF-16) thì Length là một nửa số byte vì mỗi phần tử rộng 2 byte.

Chuỗi của bạn '1 ¢' có hai điểm mã, nhưng điểm mã thứ hai yêu cầu hai byte để mã hóa nó trong UTF-8. Do đó, Length(Utf8String('1¢')) đánh giá thành ba.

Bạn đề cập đến SizeOf trong tiêu đề câu hỏi. Việc chuyển một biến chuỗi thành SizeOf sẽ luôn trả về kích thước của một con trỏ, vì biến chuỗi là, dưới mui xe, chỉ là một con trỏ.

Để câu hỏi cụ thể của bạn:

Tại sao sự khác biệt trong việc xử lý luôn luôn ở đó?

Chỉ có sự khác biệt nếu bạn nghĩ về Length liên quan đến byte. Nhưng đó là cách sai lầm để suy nghĩ về nó Length luôn luôn trả về một số phần tử, và khi được xem theo cách đó, có hành vi là thống nhất trên tất cả các loại chuỗi, và thực sự trên tất cả các loại mảng.

Tại sao Length() không làm những gì được mong đợi, chỉ trả về độ dài của tham số (như trong số lượng phần tử) thay vì cho kích thước tính bằng byte trong một số trường hợp?

Nó luôn trả về số phần tử. Nó chỉ xảy ra khi kích thước phần tử là một byte đơn, sau đó đếm phần tử và số byte xảy ra giống nhau. Trong thực tế, tài liệu mà bạn tham chiếu cũng chứa các phần sau ngay trên đoạn trích mà bạn đã cung cấp: Trả về số ký tự trong một chuỗi hoặc các phần tử trong một mảng. Đó là văn bản chính. Đoạn trích mà bạn đưa vào có nghĩa là minh họa cho các tác động của văn bản in nghiêng này.

Tại sao tiểu bang phân chia kết quả bằng 2 cho chuỗi Unicode (UTF-16)? AFAIK UTF-16 tối đa là 4 byte và do đó điều này sẽ cho kết quả không chính xác.

Yếu tố ký tự UTF-16 luôn rộng 16 bit. Tuy nhiên, một số điểm mã Unicode yêu cầu hai yếu tố ký tự để mã hóa. Các cặp yếu tố nhân vật này được gọi là cặp thay thế.


Bạn đang hy vọng, tôi nghĩ rằng, Length sẽ trả về số điểm mã trong chuỗi. Nhưng nó không. Nó trả về số lượng các phần tử ký tự. Và đối với các bảng mã có độ dài thay đổi, số lượng các điểm mã không nhất thiết giống như số lượng các phần tử ký tự. Nếu chuỗi của bạn được mã hóa dưới dạng UTF-32 thì số lượng các điểm mã sẽ giống với số phần tử ký tự vì UTF-32 là một mã hóa có kích thước không đổi.

Cách nhanh chóng để đếm các điểm mã là quét qua chuỗi kiểm tra các cặp thay thế. Khi bạn gặp một cặp thay thế, hãy đếm một điểm mã. Nếu không, khi bạn gặp phải một phần tử ký tự không phải là một phần của cặp thay thế, hãy đếm một điểm mã. Trong mã giả:

N := 0; 
for C in S do 
    if C.IsSurrogate then 
    inc(N) 
    else 
    inc(N, 2); 
CodePointCount := N div 2; 

Một điểm khác cần thực hiện là số điểm mã không giống với số ký tự hiển thị. Một số điểm mã được kết hợp các ký tự và được kết hợp với các điểm mã lân cận của chúng để tạo thành một ký tự có thể nhìn thấy hoặc glyph.

Cuối cùng, nếu tất cả các bạn đang hy vọng làm là tìm ra kích thước byte payload chuỗi, sử dụng biểu thức này:

Length(S) * SizeOf(S[1]) 

biểu hiện này làm việc cho tất cả các loại chuỗi.

Hãy rất cẩn thận về chức năng System.SysUtils.ByteLength. Đối mặt với nó, điều này dường như chỉ là những gì bạn muốn. Tuy nhiên, hàm đó trả về độ dài byte của chuỗi được mã hóa UTF-16. Vì vậy, nếu bạn vượt qua nó AnsiString, giả sử, sau đó giá trị được trả về bởi ByteLength gấp đôi số byte của AnsiString.

+1

Nhìn vào mã trong câu hỏi của tôi. "1 ¢" chỉ dài hai ký tự, nhưng đầu ra vẫn là 3. – ZzZombo

+0

@ZzZombo Đó là vì UTF-8 thay đổi từ 1 đến 4 byte để biểu diễn các ký tự. Ký tự cyrillic mà bạn viết chiếm đóng là 2 byte – EProgrammerNotFound

+0

Vâng, đó là những gì tôi đang nói đến. Nếu nó đã trả về số phần tử như mong đợi, nó sẽ là 2, nhưng nó trả về kích thước của chuỗi, đó là một công việc cho SizeOf(). – ZzZombo

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