2013-05-13 37 views
9

Tôi đang làm việc để chuyển một số mã Delphi 7 sang XE4, vì vậy, unicode là chủ đề ở đây.Độ dài chuỗi Delphi Unicode theo byte

Tôi có phương thức trong đó chuỗi được ghi vào TMemoryStream, vì vậy theo this embarcadero article, tôi nên nhân chiều dài của chuỗi (trong ký tự) với kích thước của loại Char để có độ dài bằng byte cần thiết cho tham số length (in bytes) cho WriteBuffer.

vì vậy trước khi:

rawHtml : string; //AnsiString 
... 
memorystream1.WriteBuffer(Pointer(rawHtml)^, Length(rawHtml); 

sau:

rawHtml : string; //UnicodeString 
... 
memorystream1.WriteBuffer(Pointer(rawHtml)^, Length(rawHtml)* SizeOf(Char)); 

sự hiểu biết của tôi về loại UnicodeString Delphi là nó UTF-16 trong nội bộ. Nhưng sự hiểu biết chung của tôi về Unicode là không phải tất cả các ký tự unicode đều có thể được biểu diễn ngay cả trong 2 byte, do đó một số ký tự ngoại ngữ trong trường hợp góc sẽ mất 4 byte. Another of embarcadero's articles dường như xác nhận rằng sự nghi ngờ của tôi, "Trên thực tế, thậm chí không đúng là một Char bằng hai byte!" Vì vậy ... điều đó khiến tôi băn khoăn liệu Length(rawHtml)* SizeOf(Char) có thực sự đủ mạnh để luôn chính xác hay không, hoặc liệu có cách nào tốt hơn để xác định kích thước của chuỗi sẽ chính xác hơn không? Có phải không?

+5

tại sao bạn không sử dụng 'TStringStream' thay vì' TMemoryStream'? – teran

+0

Cuối cùng, MemoryStream được chuyển đến thành phần TWebBrowser để hiển thị. Khá nhiều ví dụ tôi từng thấy trong số đó đã sử dụng MemoryStream. StringStream sẽ là một lựa chọn tốt hơn cho mục đích đó? –

+0

@Jessica Cuối cùng, cả hai đều dựa trên một 'TStream' có nghĩa là cấu trúc bên trong của cả hai làm việc giống nhau - nó chỉ là cách bạn tương tác với nó là khác nhau. Vì vậy, ngay cả một 'TFileStream' hoặc' TResourceStream' được áp dụng để sử dụng trong trường hợp của bạn, đó là, nếu bạn đang gửi tập tin hoặc tài nguyên cho trình duyệt của bạn anyway. –

Trả lời

7

Hiểu biết của tôi về loại UnicodeString của Delphi là UTF-16 nội bộ.

Bạn đúng về mã hóa UTF-16 của Delphi UnicodeString.Điều này có nghĩa là những gì một ký tự 16-bit là đủ rộng để đại diện cho tất cả code points từ các Basic Multilingual Plane chính xác như một yếu tố Char của string mảng.

Nhưng sự hiểu biết chung của tôi về Unicode là không phải tất cả các ký tự unicode thể được biểu diễn ngay cả trong 2 byte, rằng một số nhân vật trường hợp góc nước ngoài sẽ mất 4 byte.

Tuy nhiên, bạn có một chút quan niệm sai lầm ở đây. Length chức năng không thực hiện bất kỳ kiểm tra sâu sắc của các ký tự và chỉ cần trả về số 16-bit WideChar yếu tố, mà không tính đến bất kỳ thay thế trong chuỗi của bạn. Điều này có nghĩa gì nếu bạn gán một ký tự đơn từ bất kỳ Supplementary Planes đến UnicodeString, Length sẽ trở lại 2.

program Egyptian; 

{$APPTYPE CONSOLE} 

var 
    S: UnicodeString; 

begin 
    S := #$1304E; // single char 
    Writeln(Length(S)); 
    Readln; 
end. 

Kết luận: kích thước byte của dữ liệu chuỗi luôn luôn cố định và bằng Length(S) * SizeOf(Char), bất kể nếu S chứa bất kỳ ký tự có độ dài biến đổi nào.

9

Delphi của UnicodeString được mã hóa bằng UTF-16. UTF-16 là mã hóa độ dài biến, giống như UTF-8. Nói cách khác, một điểm mã Unicode duy nhất có thể yêu cầu nhiều yếu tố nhân vật để mã hóa nó. Là một điểm đáng chú ý, mã hóa Unicode có độ dài cố định duy nhất là UTF-32. Mã hóa UTF-16 sử dụng các phần tử ký tự 16 bit, do đó tên.

Trong Unicode Delphi, Char là bí danh cho WideChar là phần tử ký tự UTF-16. Và string là bí danh cho UnicodeString, là một mảng gồm các phần tử WideChar. Hàm Length() trả về số phần tử trong mảng.

Vì vậy, SizeOf(Char) luôn là 2 cho UnicodeString. Một số điểm mã Unicode được mã hóa với nhiều thành phần ký tự, hoặc Char s. Nhưng Length() trả lại số lượng thành phần ký tự và không phải số số điểm mã. Tất cả các yếu tố nhân vật đều có cùng kích thước. Vì vậy,

memorystream1.WriteBuffer(Pointer(rawHtml)^, Length(rawHtml)* SizeOf(Char)); 

là chính xác.

3

Những gì bạn đang làm là chính xác (với sizeof (Char)).

Điều bạn đề cập đến là không một ký tự nào đề cập đến một điểm mã (do cặp thay thế chẳng hạn). Nhưng các ký tự USC2 được mã hóa (KHÔNG UTF-16) trong chuỗi lấy chính xác số lượng byte với Length(Str) * sizeof(Char).

Lưu ý rằng mã hóa Unicode được sử dụng trong Delphi giống như tất cả các cuộc gọi Windows API mong đợi trong các biến thể .... W.

+0

Bạn đang nói về cái gì? Câu hỏi là về UTF16 chứ không phải về UCS2. –

+0

Trong UnicodeString UTF-16 được sử dụng, không phải UCS-2 cũ hơn. Vì vậy, một điểm mã có thể được tạo thành từ một hoặc hai Chars. Nhưng như David đã giải thích, một cặp thay thế là hai Chars, và Length đếm số lượng các phần tử Char, không phải số lượng các điểm mã. –

+0

Chuỗi Windows đã là UTF-16 kể từ Windows 2000 – afrazier

3

Những người khác đã giải thích cách mã hóa UnicodeString và cách tính độ dài byte của nó. Tôi chỉ muốn nhấn mạnh rằng, RTL đã có một chức năng như vậy - SysUtils.ByteLength():

memorystream1.WriteBuffer(PChar(rawHtml)^, ByteLength(rawHtml)); 
+0

Đây là một chức năng được thiết kế thực sự tồi tệ trong tâm trí bạn. Nó sẽ chấp nhận các chuỗi khác với UnicodeString nhưng trả lại các giá trị vô dụng. Hãy suy nghĩ qua những gì sẽ xảy ra khi bạn vượt qua nó một UTF8String. Tôi QC'ed này vô ích. –

+0

Tôi đã đọc báo cáo QC của bạn."Giải pháp" được đề xuất của bạn không tốt hơn, bởi vì việc chuyển 'UnicodeString' sang' RawByteString' vẫn thực hiện chuyển đổi dữ liệu, lần này từ UTF-16 sang Ansi, có thể mất dữ liệu. 'RawByteString' không lưu giữ dữ liệu' UnicodeString', chỉ có dữ liệu 'AnsiString (N')'. Giải pháp chính xác là quá tải 'ByteLength()' trên ** cả ** 'UnicodeString' và' RawByteString', giống như các hàm RTL khác. –

+0

Bạn nói đúng. Tôi sẽ sửa báo cáo QC của mình. Nó có lẽ là một sự lãng phí thời gian mặc dù bởi vì các báo cáo chưa bao giờ được mở ra. –