2013-05-13 26 views
9

Pracuję nad przeniesieniem kodu Delphi 7 do XE4, więc tematem jest tutaj unicode.Delphi Unicode String Length w bajtach

Mam metodę, w której ciąg jest zapisywany do TMemoryStream, więc zgodnie z this embarcadero article powinienem pomnożyć długość ciągu znaków (w znakach) razy rozmiar typu Char, aby uzyskać długość w bajtach, które są potrzebne dla parametru length (w bajtach) do WriteBuffer.

więc przed:

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

po:

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

moje rozumienie Delphi typu UnicodeString jest to, że UTF-16 wewnętrznie. Ale moje ogólne zrozumienie Unicode polega na tym, że nie wszystkie znaki Unicode mogą być reprezentowane nawet w 2 bajtach, a niektóre znaki obce przypadku zajmą 4 bajty. Another of embarcadero's articles wydaje się potwierdzać moje podejrzenia: "W rzeczywistości nie zawsze jest prawdą, że jeden znak równy jest dwóm bajtom!"

Więc ... to mnie zastanawia, czy Length(rawHtml)* SizeOf(Char) będzie wystarczająco solidny, aby być konsekwentnie dokładnym, czy też istnieje lepszy sposób określenia rozmiaru ciągu, który będzie dokładniejszy?

+5

dlaczego nie można użyć 'TStringStream' zamiast' TMemoryStream'? – teran

+0

Ostatecznie MemoryStream jest przekazywany do komponentu TWebBrowser do wyświetlenia. Prawie każdy przykład, jaki widziałem, używał MemoryStream. Czy StringStream będzie lepszym wyborem w tym celu? –

+0

@Jessica W końcu oba opierają się na 'TStream', co oznacza, że ​​wewnętrzna struktura obu działa tak samo - to właśnie sposób interakcji z nią jest inny. Tak więc nawet 'TFileStream' lub' TResourceStream' można zastosować w twoim przypadku, to znaczy, jeśli mimo to wysyłałeś pliki lub zasoby do przeglądarki. –

Odpowiedz

7

Moje rozumienie Delphi typu UnicodeString jest to, że UTF-16 wewnętrznie.

Masz rację co do kodowania UTF-16 w Delphi UnicodeString.Oznacza to, co jeden 16-bitowy znak jest wystarczająco szeroki, aby reprezentować wszystkie code points z Basic Multilingual Plane jako dokładnie jeden element Char tablicy.

Ale moje ogólne zrozumienie Unicode jest to, że nie wszystkie znaki Unicode można przedstawić nawet w 2 bajtach, że niektóre rogu liter zagranicznych zajmie 4 bajty.

Jednak masz tu trochę nieporozumień. Funkcja Length nie wykonuje żadnej głębokiej inspekcji znaków i po prostu zwraca liczbę 16-bitowych elementów WideChar, nie biorąc pod uwagę żadnych surogatek w łańcuchu. To znaczy co, jeśli przypisać pojedynczy znak z dowolnego Supplementary Planes do UnicodeString, Length powróci 2.

program Egyptian; 

{$APPTYPE CONSOLE} 

var 
    S: UnicodeString; 

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

Zawarcie: wielkość bajt danych ciąg jest zawsze stała i równa Length(S) * SizeOf(Char), bez względu na jeśli S zawiera dowolne znaki o zmiennej długości.

9

Kod Delphi UnicodeString jest kodowany z UTF-16. UTF-16 jest kodowaniem o zmiennej długości, podobnie jak UTF-8. Innymi słowy, pojedynczy punkt kodowy Unicode może wymagać wielu elementów znakowych do jego kodowania. Jako punkt zainteresowania, jedyną stałą kodowaniem Unicode jest UTF-32. Kodowanie UTF-16 wykorzystuje 16-bitowe elementy znakowe, stąd nazwa.

W języku Unicode Delphi, Char jest aliasem dla WideChar, który jest elementem znaków UTF-16. I string jest aliasem dla UnicodeString, który jest tablicą elementów WideChar. Funkcja Length() zwraca liczbę elementów w tablicy.

Tak, SizeOf(Char) jest zawsze 2 dla UnicodeString. Niektóre punkty kodu Unicode są kodowane wieloma elementami znakowymi lub Char s. Ale Length() zwraca liczbę elementów znaków i , a nie liczbę punktów kodowych. Wszystkie elementy postaci mają ten sam rozmiar. Tak więc ma być poprawna.

3

Co robisz jest poprawne (z sizeof (Char)).

To, co się odnosi, polega na tym, że żaden znak nie odnosi się do jednego punktu kodowego (na przykład z powodu par zastępczych). Jednak znaki zakodowane w standardzie USC2 (NIE UTF-16) w ciągu znaków zajmują dokładnie taką ilość bajtów z wartością Length(Str) * sizeof(Char).

Należy pamiętać, że kodowanie Unicode używane w Delphi jest takie samo, jak wszystkie połączenia Windows API oczekujące w wariantach .... W.

+0

O czym ty mówisz? Pytanie dotyczy formatu UTF16, a nie UCS2. –

+0

W UnicodeString używany jest UTF-16, a nie starszy UCS-2. Zatem punkt kodowy może składać się z jednego lub dwóch znaków. Ale jak wyjaśnił David, zastępczą parą jest dwóch Chars, a Length liczy liczbę elementów Char, a nie liczbę punktów kodowych. –

+0

Ciągi Windows mają UTF-16 od Windows 2000 – afrazier

3

Inni wyjaśnili, w jaki sposób kodowanie UnicodeString jest kodowane i jak obliczyć jego długość bajtową. Chcę tylko wspomnieć, że RTL ma już taką funkcję - SysUtils.ByteLength():

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

Jest to naprawdę źle zaprojektowana funkcja. Przyjmuje ciągi inne niż UnicodeString, ale zwraca raczej bezużyteczne wartości. Zastanów się, co się stanie, gdy przekażesz mu UTF8String. I QC'ed to bez skutku. –

+0

Przeczytałem twój raport QC.Twoje proponowane "rozwiązanie" nie jest lepsze, ponieważ przekazanie 'UnicodeString' do' RawByteString' nadal wykonuje konwersję danych, tym razem z UTF-16 do Ansi, która może być stratna. 'RawByteString' nie zachowuje danych' UnicodeString', tylko dane 'AnsiString (N'). Prawidłowym rozwiązaniem jest przeciążenie 'ByteLength()' on ** both ** 'UnicodeString' oraz' RawByteString', tak jak robią to inne funkcje RTL. –

+0

Masz całkowitą rację. Naprawię mój raport QC. To prawdopodobnie strata czasu, ponieważ raport nigdy nie został otwarty. –

Powiązane problemy