You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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内容实现,步骤如下:

  1. 先把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;
    
  2. 提取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;
    
  3. 遍历替换\'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指定的代码页做转换,具体步骤:

  1. 和上面一样,先提取\ansicpg对应的代码页编号。
  2. 整理ANSI字节流:收集所有\'XX转义对应的字节,同时保留RTF中的普通ANSI文本(非转义的字符),组成完整的ANSI字节数组。
  3. 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;
    
  4. 对于RTF中已经存在的\uXXXX序列,直接提取XXXX对应的十六进制码点,转成Unicode字符后和上述转换结果合并即可。另外要注意,\uXXXX后面可能跟着一个\'XX的兼容替代字符,解析时可以忽略这个替代字符,直接用\uXXXX的码点。

备注:内容来源于stack exchange,提问作者SOLID Developper

火山引擎 最新活动