Delphi 11.1.5中TRichEdit保存RTF时Unicode字符编码格式设置及相关字符转换问题
Delphi 11.1.5中TRichEdit保存RTF时Unicode字符编码格式设置及相关字符转换问题
嗨,针对你的问题,我从实际开发角度给你拆解解答,都是基于Delphi 11.1.5的Unicode环境和Windows RichEdit控件的特性来分析的:
一、让TRichEdit输出\uXXXX格式的Unicode转义
首先得明确:TRichEdit的Lines.SaveToStream()底层是调用Windows的EM_STREAMOUT消息生成RTF的,默认逻辑是:根据控件关联的代码页(对应RTF里的\ansicpg标记),能被该代码页表示的字符会转成\'XX格式的ANSI字节,只有无法表示的Unicode字符才会输出\uXXXX。
如果想强制所有Unicode字符都用\uXXXX输出,没有直接的属性或开关,但可以通过后处理生成的RTF内容实现,步骤如下:
- 先把TRichEdit内容保存到内存流,再读取为ASCII格式的RTF文本(因为RTF本身是ASCII兼容的):
var MS: TMemoryStream; RtfText: string; begin MS := TMemoryStream.Create; try RichEdit1.Lines.SaveToStream(MS); MS.Position := 0; // 用ASCII编码读取RTF,避免编码转换错误 RtfText := TEncoding.ASCII.GetString(MS.Memory^, MS.Size); finally MS.Free; end; end; - 提取RTF中的
\ansicpg代码页:用正则匹配\ansicpg(\d+),拿到对应的代码页编号(比如\ansicpg1252就是1252):var CodePage: UINT; Match: TMatch; begin CodePage := CP_ACP; // 默认用系统ANSI代码页 Match := TRegEx.Match(RtfText, '\\ansicpg(\d+)'); if Match.Success then CodePage := StrToUInt(Match.Groups[1].Value); end; - 遍历替换
\'XX转义:- 找到所有
\'[0-9A-Fa-f]{2}格式的序列,把每个序列转换成对应的ANSI字节(比如\'e4转成字节$E4)。 - 用
MultiByteToWideChar结合提取到的代码页,把字节转成Unicode字符。 - 把原
\'XX替换为\uXXXX(XXXX是该Unicode字符的四位十六进制码点,比如字符ä对应U+00E4,就替换成\u00e4)。
- 找到所有
注意:处理时要避开RTF控制字(比如\par、\b)里的转义,只处理表示实际字符的\'XX序列。
二、如果无法修改输出格式,如何处理\'XX转义的ANSI字符
如果不想修改RTF输出,而是直接解析\'XX序列,核心是利用\ansicpg指定的代码页做转换,具体步骤:
- 和上面一样,先提取
\ansicpg对应的代码页编号。 - 整理ANSI字节流:收集所有
\'XX转义对应的字节,同时保留RTF中的普通ANSI文本(非转义的字符),组成完整的ANSI字节数组。 - 用
MultiByteToWideChar把ANSI字节流转成Unicode字符串:function AnsicpgBytesToUnicode(const Bytes: TBytes; CodePage: UINT): string; var WideLen: Integer; begin Result := ''; if Length(Bytes) = 0 then Exit; // 先获取所需的Unicode缓冲区长度 WideLen := MultiByteToWideChar(CodePage, 0, PByte(Bytes), Length(Bytes), nil, 0); if WideLen > 0 then begin SetLength(Result, WideLen); // 执行转换 MultiByteToWideChar(CodePage, 0, PByte(Bytes), Length(Bytes), PWideChar(Result), WideLen); end; end; - 对于RTF中已经存在的
\uXXXX序列,直接提取XXXX对应的十六进制码点,转成Unicode字符后和上述转换结果合并即可。另外要注意,\uXXXX后面可能跟着一个\'XX的兼容替代字符,解析时可以忽略这个替代字符,直接用\uXXXX的码点。
备注:内容来源于stack exchange,提问作者SOLID Developper




