首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >将UnicodeString转换为AnsiString

将UnicodeString转换为AnsiString
EN

Stack Overflow用户
提问于 2014-11-12 17:03:09
回答 3查看 42.9K关注 0票数 27

在过去,我有一个函数可以将WideString转换为指定代码页的AnsiString

代码语言:javascript
运行
复制
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中的字节表示指定代码页中的字符。

例如:

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

将返回Windows-1252编码的字符串:

代码语言:javascript
运行
复制
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

代码语言:javascript
运行
复制
funtion WideStringToString(const Source: UnicodeString; CodePage: UINT): AnsiString;
begin
   ...
end;

现在我们得出返回值。我有一个包含字节的AnsiString

代码语言:javascript
运行
复制
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也秘密地包含了代码页:

  • codePage: 1258
  • 长度: 44
  • 值: The qùíçk brown fôx jumped ovêr the lázÿ dog

这一页代码是错误的。Delphi指定的是我的计算机的代码页,而不是字符串的代码页。从技术上讲,这不是一个问题,我一直都知道AnsiString在一个特定的代码页面中,我只是需要确保传递这些信息。

因此,当我想解码字符串时,我必须将代码页传递给它:

代码语言:javascript
运行
复制
s := TUnicodeHeper.StringToWideString(s, 1252);

使用

代码语言:javascript
运行
复制
function StringToWideString(s: AnsiString; CodePage: UINT): UnicodeString;
begin
   ...
   MultiByteToWideChar(...);
   ...
end;

然后一个人把一切都搞砸了

问题是,在过去,我声明了一个名为Utf8String的类型。

代码语言:javascript
运行
复制
type
   Utf8String = type AnsiString;

因为它很普遍,可以有:

代码语言:javascript
运行
复制
function TUnicodeHelper.WideStringToUtf8(const s: UnicodeString): Utf8String;
begin
   Result := WideStringToString(s, CP_UTF8);
end;

相反的是:

代码语言:javascript
运行
复制
function TUnicodeHelper.Utf8ToWideString(const s: Utf8String): UnicodeString;
begin
   Result := StringToWideString(s, CP_UTF8);
end;

现在,在XE6中,我有一个函数,作为Utf8String。如果某些现有代码采用UTF-8编码的AnsiString,并尝试使用Utf8ToWideString将其转换为UnicodeString,则会失败:

代码语言:javascript
运行
复制
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

或者更糟的是,现有代码的广度:

代码语言:javascript
运行
复制
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的内容。

代码语言:javascript
运行
复制
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应该是一个通用的接收者,而不是作为一个返回参数:

代码语言:javascript
运行
复制
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字符串,反之亦然:

代码语言:javascript
运行
复制
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:

代码语言:javascript
运行
复制
Utf8Encode('Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ');

我可以通过以下方法将UnicodeString转换为当前代码页:

代码语言:javascript
运行
复制
AnsiString('Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ');

但是如何将UnicodeString转换为任意(未指定的)代码页?

我的感觉是因为一切都是AnsiString

代码语言:javascript
运行
复制
Utf8String = AnsiString(65001);
RawByteString = AnsiString(65535);

我应该咬紧牙关,打开AnsiString结构,并将正确的代码页插入其中:

代码语言:javascript
运行
复制
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的其余部分就会排好队。

EN

Stack Overflow用户

回答已采纳

发布于 2014-11-12 17:37:16

在这种情况下,使用RawByteString是一个合适的解决方案:

代码语言:javascript
运行
复制
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()

代码语言:javascript
运行
复制
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已经知道如何为您检索和使用代码页:

代码语言:javascript
运行
复制
function StringToWideString(const Source: AnsiString): UnicodeString;
begin
  Result := UnicodeString(Source);
end;

代码语言:javascript
运行
复制
function StringToWideString(const Source: RawByteString): UnicodeString;
begin
  Result := UnicodeString(Source);
end;

尽管如此,我建议完全放弃助手函数,而只使用键入的字符串。让RTL为您处理转换:

代码语言:javascript
运行
复制
type
  Win1252String = type AnsiString(1252);

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

代码语言:javascript
运行
复制
var
  s: UnicodeString;
  u: UTF8String;
begin
  s := 'Ŧĥε qùíçķ ƀřǭŵņ fôx ǰűmpεď ōvêŗ ţħě łáƶÿ ďơǥ';
  u := UTF8String(s);
  s := UnicodeString(u);
end;
票数 19
EN
查看全部 3 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26892449

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档