You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js RTF解析模块中CJK字符与RTF转义码互转方案咨询

搞定RTF中CJK字符与十六进制转义的互转问题

我之前做过类似的RTF解析工具,你的问题核心其实是没摸透RTF里\'hh转义序列的规则——它不是直接对应UTF-8字节,而是和当前激活的字体(就是你看到的\f1、\f0)绑定的字符集挂钩的。下面给你具体的解决思路和Node.js实现:

先理清楚核心逻辑

RTF里的\'hh表示当前字符集下的一个8位字节,而CJK字符大多是双字节(比如GBK、Big5),所以你看到的\'e2\'8f其实是两个连续的单字节,组合起来才是“鈴”在对应字符集里的完整编码。要完成转换,你需要三步:

  • 先搞清楚\f1对应的字符集是什么(从RTF的字体定义表fonttbl里找)
  • 把连续的\'hh字节序列按这个字符集解码成Unicode字符
  • 反过来的话,把Unicode字符按目标字符集编码成字节,再转成\'hh格式

Node.js里的简便实现(用成熟库)

不用自己造轮子,iconv-lite这个轻量库就能搞定几乎所有字符集的转码,比内置功能好用太多。

1. 解码:把\'e2\'8f转成“鈴”

首先你得确认\f1对应的字符集,比如RTF头部的字体表可能长这样:

{\rtf1\ansi\deff0{\fonttbl{\f0\fnil\fcharset0 Arial;}{\f1\fnil\fcharset136 PMingLiU;}}}

这里的\fcharset136就代表\f1用的是Big5字符集(136是Big5的官方字符集代码)。假设是Big5,代码如下:

const iconv = require('iconv-lite');

// 从RTF里提取的字节序列:e2 8f
const rtfBytes = Buffer.from(['e2', '8f'], 'hex');
// 按Big5解码成Unicode字符
const cjkChar = iconv.decode(rtfBytes, 'big5');
console.log(cjkChar); // 输出:鈴

如果是GBK的话,把'big5'换成'gbk'就行,其他字符集同理。

2. 编码:把“鈴”转成\'e2\'8f

还是以Big5为例,反向转换的代码:

const iconv = require('iconv-lite');

const targetChar = '鈴';
// 按Big5编码成字节
const charBytes = iconv.encode(targetChar, 'big5');
// 转成RTF要求的\'hh格式
const rtfEscapeStr = charBytes.reduce((result, byte) => {
  return result + `\\'${byte.toString(16).padStart(2, '0')}`;
}, '');
console.log(rtfEscapeStr); // 输出:\'e2\'8f

3. 怎么快速确定字体对应的字符集?

RTF的字体表fonttbl里的\fcharsetN就是关键,N是微软定义的字符集代码,常见的CJK相关代码对应:

  • GBK/GB2312:134
  • Big5:136
  • Shift-JIS(日文):129
  • UTF-16:128

你在解析RTF的时候,先把fonttbl里每个\fN对应的\fcharset值存下来,转成对应的字符集名称(比如136 → 'big5'),后续处理转义序列的时候直接用就行。

踩过的坑提醒你

  • 别直接把\'hh当成UTF-8字节!我一开始也犯过这个错,结果转出来全是乱码,因为RTF的转义是跟着当前字体走的
  • 如果RTF里没明确写\fcharset,默认是Windows-1252(ANSI),但CJK字符肯定不会用这个,所以一定要找fonttbl的定义
  • 有些RTF会用\uN来表示Unicode字符(比如\u37329就是“鈴”),这种直接用String.fromCodePoint(37329)就能转,但你遇到的是多字节转义,还是得靠字符集转换

内容的提问来源于stack exchange,提问作者DjH

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:50:03