在 UnicodeString 中存储 UTF-8 字符串

问题描述 投票:0回答:4

在 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 的内容,即它保留为存储在 WideString 中的 UTF-8 编码字符串。

但是在 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;
string delphi unicode utf-8 utf-16
4个回答
14
投票

您的原始 Delphi 2007 代码使用 ANSI 代码页将 UTF-8 字符串转换为宽字符串。要在 Delphi 2010 中执行相同的操作,您应该使用 SetCodePage 并将 Convert 参数设置为 false。

var
  UnicodeStr: UnicodeString;
  UTF8Str: RawByteString;
begin
  UTF8Str := UTF8Encode('some unicode text');
  SetCodePage(UTF8Str, CP_ACP, False);
  UnicodeStr := UTF8Str;
  Windows.SomeFunction(PWideChar(UnicodeStr), ...)

3
投票

嗯,你为什么要这么做?为什么要将 WideString 编码为 UTF-8 只是为了将其再次存储回 WideString。显然您正在使用 Windows API 的 Unicode 版本。所以不需要使用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。在 Delphi 的 Unicode 版本中,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
投票

哪个 Windows API 调用希望您传递 UTF-8 字符串?它可以是 ANSI 字符串,也可以是 Widestring(A 或 W 函数)。宽字符串每个字符有两个字节,UTF-8 字符串有一个字节(如果超出前 128 个 ASCII 字符,则有更多字节)。

Widestring 中的 UTF-8 根本没有意义。当确实有一个 Windows 函数需要一个指向 UTF-8 字符串的指针时,您可能必须将 is 转换为 PAnsiChar。


0
投票

感谢“佐伊·彼得森”。修复“从‘RawByteString’隐式字符串转换为‘string’”:

UTF8Str := UTF8Encode('some unicode text');
SetCodePage(UTF8Str, 0, False);
// Use
Obj.Send(UnicodeString(UTF8Str));
© www.soinside.com 2019 - 2024. All rights reserved.