【问题标题】:Storing UTF-8 string in a UnicodeString将 UTF-8 字符串存储在 UnicodeString 中
【发布时间】:2010-04-23 10:38:45
【问题描述】:

在 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;

【问题讨论】:

  • 我用解决方案更新了我的答案。
  • 仅供参考,原始 2007 代码确实会干扰 UTF-8 数据。 2007 年,UTF8Encode() 返回了一个 UTF-8 编码的AnsiString。在每个版本中,将AnsiString 分配给WideString 会使用操作系统默认的Ansi 代码页执行Ansi->UTF16 转换。最后的 WideString 不包含 UTF-8 数据。它包含 UTF-16 数据。转换没有 UTF-8 存在的概念,因此如果原始输入使用任何非 ASCII 字符,则可能会损坏数据。

标签: string delphi unicode utf-8 utf-16


【解决方案1】:

您原来的 Delphi 2007 代码使用 ANSI 代码页将 UTF-8 字符串转换为宽字符串。要在 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), ...)

【讨论】:

  • 不错。不知道这个:)
【解决方案2】:

嗯,你为什么要这么做?为什么要将 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;

我检查过,它的工作原理是一样的。因为我直接在内存中移动字节,所以没有在后台进行代码页转换。我确信它可以更优雅地完成,但重点是我认为这是实现您想要实现的目标的方式。

【讨论】:

  • 是的,在将临时 AnsiString 分配给最终的 WideString 时,在最后一行完成了代码页转换。原始 D2007 代码也是如此。但附带说明一下,您可以通过在RawByteString 上使用SetCodePage() 来避免临时AnsiString,然后您可以将RawByteString 分配给WideString
【解决方案3】:

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

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

【讨论】:

  • 这是一些使用 INI 文件的(损坏的)遗留代码。因此,例如,该部分将作为 UTF8 字符串传递。我知道这是错误的,但我需要保持这样才能导入旧的设置文件。如果我为部分名称传递 Unicode,那么它将不匹配。我不能使用 ANSI 版本,因为文件名是 Unicode。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-08
  • 2011-04-26
  • 2014-12-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多