在过去,我有一个函数可以将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;
一切都成功了。我向函数传递了一个unicode字符串(即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
(后)但是Windows WideChartoMultiByte
在最佳匹配映射方面做得很好,就像它设计的那样。
现在以后的日子
现在我们在后来居上。WideString
现在是一个被抛弃的人,UnicodeString
是善良的。这是一个无关紧要的变化;因为WideChar
函数只需要一个指向一系列UnicodeString
的指针(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 XE6中,一个AnsiString
也秘密地包含了代码页:
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中,我有一个函数,将作为Utf8String
。如果某些现有代码采用UTF-8编码的AnsiString
,并尝试使用Utf8ToWideString
将其转换为UnicodeString,则会失败:
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
)中。如何向前迈进
理想情况下,我的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
,那么德尔福肯定已经有了一个函数,它可以将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
和相反的代码。
tl;dr:
我可以通过以下操作将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的其余部分就会排好队。
发布于 2014-11-12 17:37:16
在这种情况下,使用RawByteString
是一个合适的解决方案:
function WideStringToString(const Source: UnicodeString; CodePage: UINT): RawByteString;
var
strLen: Integer;
begin
strLen := LocaleCharsFromUnicode(CodePage, 0, PWideChar(Source), Length(Source), nil, 0, nil, nil));
if strLen > 0 then
begin
SetLength(Result, strLen);
LocaleCharsFromUnicode(CodePage, 0, PWideChar(Source), Length(Source), PAnsiChar(Result), strLen, nil, nil));
SetCodePage(Result, CodePage, False);
end;
end;
通过这种方式,RawByteString
保存代码页,并将RawByteString
分配给任何其他字符串类型,无论是AnsiString
还是UTF8String
或其他任何类型的字符串,都将允许RTL自动将RawByteString
数据从其当前代码页转换为目标字符串的代码页(这包括到UnicodeString
的转换)。
如果您绝对必须返回一个AnsiString
(我不建议这样做),您仍然可以通过类型广播使用SetCodePage()
:
function WideStringToString(const Source: UnicodeString; CodePage: UINT): AnsiString;
var
strLen: Integer;
begin
strLen := LocaleCharsFromUnicode(CodePage, 0, PWideChar(Source), Length(Source), nil, 0, nil, nil));
if strLen > 0 then
begin
SetLength(Result, strLen);
LocaleCharsFromUnicode(CodePage, 0, PWideChar(Source), Length(Source), PAnsiChar(Result), strLen, nil, nil));
SetCodePage(PRawByteString(@Result)^, CodePage, False);
end;
end;
相反的情况要容易得多,只需使用已经存储在(Ansi|RawByte)String
中的代码页(只需确保这些代码页总是准确的),因为RTL已经知道如何为您检索和使用代码页:
function StringToWideString(const Source: AnsiString): UnicodeString;
begin
Result := UnicodeString(Source);
end;
function StringToWideString(const Source: RawByteString): UnicodeString;
begin
Result := UnicodeString(Source);
end;
尽管如此,我建议完全放弃助手函数,而只使用键入的字符串。让RTL为您处理转换:
type
Win1252String = type AnsiString(1252);
var
s: UnicodeString;
a: Win1252String;
begin
s := 'Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ';
a := Win1252String(s);
s := UnicodeString(a);
end;
var
s: UnicodeString;
u: UTF8String;
begin
s := 'Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ';
u := UTF8String(s);
s := UnicodeString(u);
end;
https://stackoverflow.com/questions/26892449
复制相似问题