UTF-8编码下的字符串转字节数组?

8

如何将WideString(或其他长字符串)转换为UTF-8字节数组?

6个回答

13

像这样的函数将会满足您的需求:

function UTF8Bytes(const s: UTF8String): TBytes;
begin
  Assert(StringElementSize(s)=1);
  SetLength(Result, Length(s));
  if Length(Result)>0 then
    Move(s[1], Result[0], Length(s));
end;
你可以使用任何类型的字符串调用它,RTL 将从传递的字符串编码转换为 UTF-8。因此,请不要被欺骗认为您必须在调用之前转换为 UTF-8,只需传入任何字符串,让 RTL 来处理就行了。
之后是相当标准的数组复制。请注意断言,它明确指出了对于 UTF-8 编码的字符串元素大小的假设。
如果你想获取零终止器,你应该这样写:
function UTF8Bytes(const s: UTF8String): TBytes;
begin
  Assert(StringElementSize(s)=1);
  SetLength(Result, Length(s)+1);
  if Length(Result)>0 then
    Move(s[1], Result[0], Length(s));
  Result[high(Result)] := 0;
end;

1
@Cosmin 不会的。这就是关于断言的事情! - David Heffernan
一个问题..我需要添加哪个单位来使用StringElementSize()?(lazarus)。抱歉问这种问题,我是新手。 - Mariusz
@Mariusz,你的“lazarus”语句是什么意思?你在问题上打了Delphi标签。在Delphi中,它在system.pas文件中,并且由所有单元自动使用。 - David Heffernan
@Mariusz: 你可以删除整个“Assert...”行。但是,由于您标记了您的问题为Delphi不是free-pascal,@David的答案适用于Delphi而不适用于Free Pascal。但是上面的代码可能也适用于Free Pascal。我不知道。你可以试一下。 - Andreas Rejbrand
这是D2009+特定的代码,因此无法在遵循D2009之前语义的FPC上运行。将widestring(参见原始问题)传递给“UTF8string”将将其更改为本地编码(而不是像D2009+中的UTF-8),从而使字符串混乱。FPC有专门记录的函数可供使用,请参见单独的答案。 - Marco van de Voort

9
您可以在SysUtils.pas中使用TEncoding.UTF8.GetBytes

5
请注意,如果输入字符串已经编码为UTF-8,使用GetBytes将非常浪费。编译器将把输入字符串转换为UnicodeString,因为这是GetBytes允许的唯一字符串参数,而GetBytes将把字符再次转换为UTF-8以生成其结果。 - Rob Kennedy

5
如果您正在使用Delphi 2009或更高版本(Unicode版本),将WideString转换为UTF8String只需一个简单的赋值语句:
var
  ws: WideString;
  u8s: UTF8String;

u8s := ws;

编译器会调用正确的库函数进行转换,因为它知道UTF8String类型的值具有“代码页”CP_UTF8。在Delphi 7及更高版本中,您可以使用提供的库函数Utf8Encode。对于更早的版本,您可以从其他库(例如JCL)中获取该函数。您还可以使用Windows API编写自己的转换函数:
function CustomUtf8Encode(const ws: WideString): UTF8String;
var
  n: Integer;
begin
  n := WideCharToMultiByte(cp_UTF8, 0, PWideChar(ws), Length(ws), nil, 0, nil, nil);
  Win32Check(n <> 0);
  SetLength(Result, n);
  n := WideCharToMultiByte(cp_UTF8, 0, PWideChar(ws), Length(ws), PAnsiChar(Result), n, nil, nil);
  Win32Check(n = Length(Result));
end;

很多时候,你可以简单地将UTF8String用作数组,但如果你真的需要一个字节数组,你可以使用David和Cosmin的函数。如果你正在编写自己的字符转换函数,你可以跳过UTF8String,直接转换为字节数组;只需将返回类型更改为TBytes或array of Byte即可。(如果你想要数组以null结尾,则还可以增加长度。SetLength将隐式地对字符串执行此操作,但对于数组则不会。)
如果你有其他既不是WideString、UnicodeString也不是UTF8String的字符串类型,则将其转换为UTF-8的方法是先将其转换为WideString或UnicodeString,然后再将其转换回UTF-8。

4
var S: UTF8String;
    B: TBytes;

begin
  S := 'Șase sași în șase saci';
  SetLength(B, Length(S)); // Length(s) = 26 for this 22 char string.
  CopyMemory(@B[0], @S[1], Length(S));
end.

根据您需要字节的用途,您可能想要包含一个NULL终止符。

对于生产代码,请确保测试空字符串。添加所需的3-4行代码将使示例更难阅读。


1
该字符串不为空。它包含值“'Șase sași în șase saci'”。 - Cosmin Prund
+1. 并非每个人(至少可以这么说!)都知道 Length 函数的工作原理! - Andreas Rejbrand
@Cosmin 我可以看到字符串不是空的。我只是有一种感觉,即 OP 可能对除了“'Șase sași în șase saci'”之外的文本感兴趣。 - David Heffernan
@Cosmin,@David:肯定是@Cosmin开了个玩笑!(事实上,David的观点非常好。) - Andreas Rejbrand
我想通过套接字将字节发送到我的Java应用程序。 - Mariusz

1
我有以下两个例程(源代码可在此处下载-http://www.csinnovations.com/framework_utilities.htm):
function CsiBytesToStr(const pInData: TByteDynArray; pStringEncoding: TECsiStringEncoding; pIncludesBom: Boolean): string;
function CsiStrToBytes(const pInStr: string; pStringEncoding: TECsiStringEncoding; pIncludeBom: Boolean): TByteDynArray;

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接