В 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;
Ваш исходный код 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), ...)
Хм, зачем ты это делаешь? Почему вы кодируете 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 и строка (UnicodeString) похожи, но новая UnicodeString работает быстрее, потому что она подсчитывается по ссылкам, а WideString - нет.
Ваш код был неправильным, потому что строка UTF-8 имеет переменное количество байтов на символ. «A» хранится как один байт. Просто байтовый код 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;
Я проверил, и он работает точно так же.Поскольку я перемещаю байты непосредственно в памяти, преобразование кодовой страницы в фоновом режиме не выполняется. Я уверен, что это можно сделать с большей элегантностью, но дело в том, что я вижу в этом путь к тому, чего вы хотите достичь.
Какой вызов Windows API хочет, чтобы вы передали строку UTF-8? Это либо строка ANSI, либо широкая строка (функции A или W). Широкие строки имеют два байта на символ, а строки UTF-8 имеют один (или несколько, если вы за пределами первые 128 символов ASCII).
UTF-8 в Widestring просто не имеет смысла. Когда действительно есть функция Windows, которой нужен указатель на строку UTF-8, yo Вы, вероятно, должны передать это в PAnsiChar.