System.AnsiToUtf8转换不符合预期及特殊修复方法的原因咨询
问题背景
我需要将字符串转换为UTF-8字节对应的显示形式,最初使用System.AnsiToUtf8函数,它的实现如下:
function AnsiToUtf8(const S: string): _RawByteStr; begin Result := Utf8Encode(S); end;
后来为排查问题,封装了逻辑完全一致的自定义函数:
function MyAnsiToUtf8(S: string): RawByteString; begin Result := System.AnsiToUtf8(S); end;
但调用LabeledEdit2.Text := String(MyAnsiToUtf8('äöü'));时,输入框始终显示原字符äöü,无论直接传参还是使用变量都一样。
尝试了一个奇怪的写法后,却得到了预期的结果äöü:
function MyAnsiToUtf8(S: string): RawByteString; begin Result := 'x' + System.AnsiToUtf8(S); delete(Result, 1, 1) end;
原因解析
最初调用无效的原因
在Delphi 2009及后续版本中,String默认是UnicodeString,而RawByteString是带有代码页标记的字节串。System.AnsiToUtf8返回的结果会被标记为UTF-8代码页(编号65001)。当把这个带UTF-8标记的RawByteString强制转换为UnicodeString时,Delphi会自动执行反向编码转换:将UTF-8字节序列转回对应的Unicode字符,所以最终显示的还是原字符äöü,而非UTF-8字节对应的显示效果。
加'x'再删除生效的原因
执行'x' + System.AnsiToUtf8(S)时,'x'是带有默认ANSI代码页标记的RawByteString。Delphi的字符串拼接规则会将两个不同代码页的RawByteString统一转换为默认ANSI代码页(而非保留UTF-8标记),此时拼接后的结果代码页已不是UTF-8。
删除第一个字符后,得到的RawByteString依然保留默认ANSI代码页标记。此时转换为UnicodeString时,Delphi会按默认ANSI代码页解析这些字节为Unicode字符,而非执行UTF-8反向转换,因此会显示出UTF-8字节对应的äöü。
内容的提问来源于stack exchange,提问作者Wittenborner

