在 Delphi 中将宽字符代码点的十六进制字符串表示形式转换为宽字符

Posted

技术标签:

【中文标题】在 Delphi 中将宽字符代码点的十六进制字符串表示形式转换为宽字符【英文标题】:Converting a hex string representation of a widechar codepoint to a widechar in Delphi 【发布时间】:2021-07-21 06:35:39 【问题描述】:

我在将编辑框中的文本转换为 WideChar 时遇到问题。这被用于打印表情符号的代码中。

如果我手动设置 WideChar 值,如下所示

Emoji[1] := WideChar($D83D);
Emoji[2] := WideChar($DC4D);

但我希望能够通过如下编辑框设置十六进制代码

StringToWideChar(edit1.text, @wc1, Length(edit1.text));
StringToWideChar(edit2.text, @wc2, Length(edit2.text));
Emoji[1] := wc1;
Emoji[2] := wc2;

wc1 和 wc2 被定义为 WideChar。编辑框包含与上面硬编码相同的值。该代码导致空白输出,因此转换有问题。

我做错了什么?感谢您的帮助。

【问题讨论】:

为什么不直接在TEdit 中输入实际的表情符号,然后按原样使用它的TextStringToWideChar() 没有做你认为的事情。它用于将String 转换为等效长度的WideChar[] 缓冲区(即,最初用于将AnsiString 转换为PWideChar,现在只是一个普通副本)。它并不意味着将整个 String 分成一个 WideChar 10.4 所以 Andreas 的代码(带有 Remy 的编辑)工作正常。 Emoji[1]:=char(strtoint(edit1.text)); 为什么要为每个 UTF-16 代码单元使用单独的 TEdit?为什么不使用单个 TEdit 输入整个代码点?如果您不希望用户输入实际的 Emoji 符号,则至少输入其代码点值(即'$1F44D'),然后您可以使用StrToInt() 将其转换为整数,然后使用TCharacter.ConvertFromUtf32() 或@ 987654323@ 将其转换为正确的string 感谢雷米的提示。使用整个代码点并使用 ConvertFromUtf32 进行转换是更简洁的代码。 【参考方案1】:

您不能将字符串 '$D83D' 解释为文本 - 相反,您必须将其解析为整数。

首先,您需要从编辑框中获取文本。这是Edit1.Text。然后您需要将其转换为整数。例如,您可以使用StrToIntTryStrToInt。然后你只需要将这个整数重新解释(转换)为Char

procedure TForm1.Edit1Change(Sender: TObject);
var
  CodeUnit: Integer;
begin
  if TryStrToInt(Edit1.Text, CodeUnit) and InRange(CodeUnit, 0, $FFFF) then
    Label1.Caption := Char(CodeUnit)
  else
    Label1.Caption := '';
end;

在这里,作为奖励,我还使用InRange 验证假定的代码单元是一个实际的 16 位无符号整数(我的意思是,理论上用户可以输入 123456789)。 Delphi 的StrToInt 函数支持使用美元符号表示法的十六进制。

【讨论】:

从技术上讲,这实际上根本不与 codepoints 一起使用,而是与 codeunits 一起使用。这是一个重要的区别。 U+1F44D (?) 是 codepointD83D DC4D 是 UTF-16 codeunit 序列(又名 代理对)。但是,是的,0..$FFFF 是 UTF-16 codeunit 的正确范围,这是 Delphi 的 WideChar 所代表的。而且您应该使用Char() 类型转换,而不是Chr() @RemyLebeau:为什么你更喜欢Char 而不是Chr 好吧,一方面,因为Chr() 文档说。其次,因为当X128..255 时,不能保证Chr(X) 总是返回值为XChar,而Char(X) 则保证返回。 @RemyLebeau:您能否举一个 Delphi 2009+ 代码的示例,其中 ChrChar 在给出0..$FFFF 范围内的整数时不会产生相同的结果?我部分同意你的两个理由,但只是部分同意(documentation 并不是说​​你不能使用Chr,实际上我认为我没有看到有什么不同)。 例如,我见过Chr(128) 可能返回Char($20AC) 的情况(0x80 是某些字符集中的欧元符号)。而Char(128) 始终是Char($80)

以上是关于在 Delphi 中将宽字符代码点的十六进制字符串表示形式转换为宽字符的主要内容,如果未能解决你的问题,请参考以下文章

在 Delphi 2009 中将字符串转换为 PAnsiChar

如何在 Delphi 中将浮点数转换为字符串,指定有效数字,而不是十进制数字(而不是 G 格式)

Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)

在 Rust 中将二进制字符串转换为带有前导零的十六进制字符串

如何在 C++ 中将字符的十进制代码转换为 Unicode 字符串?

在 Groovy 中将整数转换为十六进制字符串