Преобразование UnicodeString в AnsiString

В старые времена у меня была функция, которая преобразуетWideString дляAnsiString указанной кодовой страницы:

function WideStringToString(const Source: WideString; CodePage: UINT): AnsiString;
...
begin
   ...
    // Convert source UTF-16 string (WideString) to the destination using the code-page
    strLen := WideCharToMultiByte(CodePage, 0,
        PWideChar(Source), Length(Source), //Source
        PAnsiChar(cpStr), strLen, //Destination
        nil, nil);
    ...
end;

И все заработало. Я прошел функциююникода строка (то есть данные в кодировке UTF-16) и преобразует ее вAnsiStringс пониманием того, что байты вAnsiString представленные символы из указанной кодовой страницы.

Например:

TUnicodeHelper.WideStringToString('Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ', 1252);

вернетWindows-1252 закодированная строка:

The qùíçk brown fôx jumped ovêr the lázÿ dog

Примечание: информация, конечно, была потеряна во время преобразования из полного набора символов Unicode в ограниченные пределы кодовой страницы Windows-1252:

Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ (до)The qùíçk brown fôx jumped ovêr the lázÿ dog (после)

Но виндаWideChartoMultiByte неплохо справляется с картированием наилучшим образом; как и задумано.

Теперь после

Теперь мы в более поздние времена.WideString теперь пария, сUnicodeString быть добром. Это несущественное изменение; так как функция Windows нужна толькоуказатель к серииWideChar в любом случае (которыйUnicodeString также является). Таким образом, мы изменим декларацию, чтобы использоватьUnicodeString вместо:

funtion WideStringToString(const Source: UnicodeString; CodePage: UINT): AnsiString;
begin
   ...
end;

Теперь мы подошли к возвращаемому значению. у меня естьAnsiString который содержит байты:

54 68 65 20 71 F9 ED E7  The qùíç
6B 20 62 72 6F 77 6E 20  k brown 
66 F4 78 20 6A 75 6D 70  fôx jump
65 64 20 6F 76 EA 72 20  ed ovêr 
74 68 65 20 6C E1 7A FF  the lázÿ
20 64 6F 67               dog

В старые времена это было хорошо. Я отслеживал, какая кодовая страницаAnsiString фактически содержится; Мне пришлосьПомните что вернулсяAnsiString не был закодирован с использованием локали компьютера (например, Windows 1258), но вместо этого был закодирован с использованием другой кодовой страницы (CodePage кодовая страница).

Но в Delphi XE6AnsiString также тайно содержит кодовую страницу:

CODEPAGE: 1258длина: 44значение: The qùíçk brown fôx jumped ovêr the lázÿ dog

Эта кодовая страница неверна. Delphi указывает кодовую страницу моего компьютера, а не кодовую страницу, которой является строка. Технически это не проблема, я всегда понимал, чтоAnsiString был в определенной кодовой странице, я просто должен был обязательно передать эту информацию.

Поэтому, когда я хотел декодировать строку, мне нужно было передать кодовую страницу вместе с ней:

s := TUnicodeHeper.StringToWideString(s, 1252);

с

function StringToWideString(s: AnsiString; CodePage: UINT): UnicodeString;
begin
   ...
   MultiByteToWideChar(...);
   ...
end;
Тогда один человек все испортил

Проблема заключалась в том, что в старые времена я объявил тип под названиемUtf8String:

type
   Utf8String = type AnsiString;

Потому что это было достаточно распространенным, чтобы иметь:

function TUnicodeHelper.WideStringToUtf8(const s: UnicodeString): Utf8String;
begin
   Result := WideStringToString(s, CP_UTF8);
end;

и наоборот:

function TUnicodeHelper.Utf8ToWideString(const s: Utf8String): UnicodeString;
begin
   Result := StringToWideString(s, CP_UTF8);
end;

Теперь в XE6 у меня есть функция, котораяпринимает a Utf8String, Если какой-то существующий код где-то был взят в кодировке UTF-8AnsiStringи попробуйте преобразовать его в UnicodeString, используяUtf8ToWideString не получится

s: AnsiString;
s := UnicodeStringToString('Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ', CP_UTF8);

...

 ws: UnicodeString;
 ws := Utf8ToWideString(s); //Delphi will treat s an CP1252, and convert it to UTF8

Или, что еще хуже, широта существующего кода, который делает:

s: Utf8String;
s := UnicodeStringToString('Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ', CP_UTF8);

Возвращенная строка станет полностью искаженной:

функция возвращаетAnsiString(1252) (AnsiString помечены как закодированные с использованием текущей кодовой страницы)возвращаемый результат сохраняется вAnsiString(65001) строка (Utf8String)Delphi преобразует строку в кодировке UTF-8 в UTF-8, как если бы это было 1252.Как двигаться вперед

В идеале мойUnicodeStringToString(string, codePage) функция (которая возвращаетAnsiString) мог бы установитьCodePage внутри строки, чтобы соответствовать фактической кодовой странице, используя что-то вродеSetCodePage:

function UnicodeStringToString(s: UnicodeString; CodePage: UINT): AnsiString;
begin
   ...
   WideCharToMultiByte(...);
   ...

   //Adjust the codepage contained in the AnsiString to match reality
   //SetCodePage(Result, CodePage, False); SetCodePage only works on RawByteString
   if Length(Result) > 0 then
      PStrRec(PByte(Result) - SizeOf(StrRec)).codePage := CodePage;
end;

За исключением того, что вручную копаться с внутренней структуройAnsiString ужасно опасно

Так что насчет возвращенияRawByteString?

Это было сказано, много раз, многие люди, которые не я, чтоRawByteString должен бытьуниверсальный получатель; он не должен быть параметром возврата:

function UnicodeStringToString(s: UnicodeString; CodePage: UINT): RawByteString;
begin
   ...
   WideCharToMultiByte(...);
   ...

   //Adjust the codepage contained in the AnsiString to match reality
   SetCodePage(Result, CodePage, False); SetCodePage only works on RawByteString
end;

Это позволяет использовать поддерживаемые и документированныеSetCodePage.

Но если мы собираемся пересечь линию и начать возвращатьсяRawByteStringКонечно, Delphi уже имеет функцию, которая может преобразоватьUnicodeString кRawByteString строка и наоборот:

function WideStringToString(const s: UnicodeString; CodePage: UINT): RawByteString;
begin
   Result := SysUtils.Something(s, CodePage);
end;

function StringToWideString(const s: RawByteString; CodePage: UINT): UnicodeString;
begin
   Result := SysUtils.SomethingElse(s, CodePage);       
end;

Но что это?

Или что еще я должен сделать?

Это был скучный фон для тривиального вопроса.реальный вопрос, конечно, что я должен делать вместо этого? Существует много кода, который зависит отUnicodeStringToString и наоборот.

ТЛ; др:

Я могу преобразоватьUnicodeString в UTF, выполнив:

Utf8Encode('Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ');

и я могу преобразоватьUnicodeString к текущей кодовой странице, используя:

AnsiString('Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ');

Но как мне преобразоватьUnicodeString к произвольной (неуказанной) кодовой странице?

Я чувствую, что так как все на самом делеAnsiString:

Utf8String = AnsiString(65001);
RawByteString = AnsiString(65535);

я должен укусить пулю, перебитьAnsiString структуру и вставьте в нее правильную кодовую страницу:

function StringToAnsi(const s: UnicodeString; CodePage: UINT): AnsiString;
begin
   LocaleCharsFromUnicode(CodePage, ..., s, ...);

   ...

   if Length(Result) > 0 then
      PStrRec(PByte(Result) - SizeOf(StrRec)).codePage := CodePage;
end;

Тогда остальные VCL попадут в линию.

Ответы на вопрос(3)

Ваш ответ на вопрос