在UnicodeString中存储UTF-8字符串

时间:2010-04-23 10:38:46

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

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

3 个答案:

答案 0 :(得分:13)

您的原始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), ...)

答案 1 :(得分: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和string(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;

我检查过,它的工作原理是一样的。因为我直接在内存中移动字节,所以后台没有进行代码页转换。我相信它可以通过更大的元素来完成,但关键是我认为这是你想要实现的目标。

答案 2 :(得分:0)

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

Widestring中的UTF-8没有意义。当真的有一个想要指向UTF-8字符串的Windows函数时,你可能需要将其转换为PAnsiChar。