Хранение строки UTF-8 в UnicodeString - PullRequest
9 голосов
/ 23 апреля 2010

В Delphi 2007 вы можете сохранить строку UTF-8 в WideString, а затем передать ее в функцию Win32, например,

var
  UnicodeStr: WideString;
  UTF8Str: WideString;
begin
  UnicodeStr:='some unicode text';
  UTF8Str:=UTF8Encode(UnicodeStr);
  Windows.SomeFunction(PWideChar(UTF8Str), ...)
end;

Delphi 2007 не вмешивается в содержимое UTF8Str, т.е.оставленный как строка в кодировке UTF-8, хранящаяся в WideString.

Но в Delphi 2010 я изо всех сил пытаюсь найти способ сделать то же самое, то есть сохранить строку в кодировке UTF-8 в WideString без нееавтоматически конвертируется из UTF-8.Я не могу передать указатель на строку UTF-8 (или RawByteString), например следующее явно не будет работать:

var
  UnicodeStr: WideString;
  UTF8Str: UTF8String;
begin
  UnicodeStr:='some unicode text';
  UTF8Str:=UTF8Encode(UnicodeStr);
  Windows.SomeFunction(PWideChar(UTF8Str), ...)
end;

Ответы [ 3 ]

13 голосов
/ 23 апреля 2010

Ваш исходный код Delphi 2007 преобразовывал строку UTF-8 в широкую строку с использованием кодовой страницы ANSI.Чтобы сделать то же самое в Delphi 2010, вы должны использовать SetCodePage с параметром Convert false.

var
  UnicodeStr: UnicodeString;
  UTF8Str: RawByteString;
begin
  UTF8Str := UTF8Encode('some unicode text');
  SetCodePage(UTF8Str, 0, False);
  UnicodeStr := UTF8Str;
  Windows.SomeFunction(PWideChar(UnicodeStr), ...)
3 голосов
/ 23 апреля 2010

Хм, почему ты это делаешь? Почему вы кодируете WideString в UTF-8 только для того, чтобы снова сохранить его обратно в WideString. Вы, очевидно, используете Unicode-версию Windows API. Поэтому нет необходимости использовать строку в кодировке UTF-8. Или я что-то упустил.

Поскольку функции Windows API являются либо Unicode (два байта), либо ANSI (один байт). UTF-8 здесь был бы неправильным выбором, потому что в основном он содержит один байт на символ, но для символов выше основания ASCII он использует два или более байтов.

В противном случае эквивалент для вашего старого кода в Unicode Delphi будет:

var
  UnicodeStr: string;
  UTF8Str: string;
begin
  UnicodeStr:='some unicode text';
  UTF8Str:=UTF8Encode(UnicodeStr);
  Windows.SomeFunction(PWideChar(UTF8Str), ...)
end;

WideString и string (UnicodeString) похожи, но новая UnicodeString быстрее, потому что она подсчитывается, а WideString - нет.

Ваш код был неправильным, потому что строка UTF-8 имеет переменное число байтов на символ. «А» хранится как один байт. Просто байт-код ASCII. «ü», с другой стороны, будет храниться как два байта. А поскольку вы затем используете PWideChar, функция всегда ожидает два байта на символ.

Есть еще одно отличие. В более старых версиях Delphi (ANSI) Utf8String был просто AnsiString. В Unicode-версиях Delphi Utf8String представляет собой строку с кодовой страницей UTF-8 за ней. Так что ведет себя по-разному.

Старый код все равно будет работать правильно:

var
  UnicodeStr: WideString;
  UTF8Str: WideString;
begin
  UnicodeStr:='some unicode text';
  UTF8Str:=UTF8Encode(UnicodeStr);
  Windows.SomeFunction(PWideChar(UTF8Str), ...)
end;

Он будет действовать так же, как в Delphi 2007. Так что, возможно, у вас есть проблема в другом месте.

Мик, ты прав. Компилятор делает некоторую дополнительную работу за кулисами. Поэтому, чтобы избежать этого, вы можете сделать что-то вроде этого:

var
  UTF8Str: AnsiString;
  UnicodeStr: WideString;
  TempString: RawByteString;
  ResultString: WideString;
begin
  UnicodeStr := 'some unicode text';
  TempString := UTF8Encode(UnicodeStr);
  SetLength(UTF8Str, Length(TempString));
  Move(TempString[1], UTF8Str[1], Length(UTF8Str));
  ResultString := UTF8Str;
end;

Я проверил, и все работает точно так же. Поскольку я перемещаю байты непосредственно в память, в фоновом режиме не выполняется преобразование кодовой страницы. Я уверен, что это можно сделать с большей элегантностью, но дело в том, что я вижу в этом путь к тому, чего вы хотите достичь.

0 голосов
/ 23 апреля 2010

Какой вызов Windows API хочет, чтобы вы передали строку UTF-8?Это либо строка ANSI, либо широкая строка (функции A или W).Широкие строки имеют два байта на символ, а строки UTF-8 имеют один (или больше, если вы выходите за пределы первых 128 символов ASCII).

UTF-8 в широкой строке просто не имеет смысла.Когда действительно есть функция Windows, которая хочет указатель на строку UTF-8, вам, вероятно, придется привести это к PAnsiChar.

...