PHP读取RTF乱码的根本原因是RTF为带控制指令的富文本格式,需先解析\ansicpg声明的代码页,再剥离控制指令,最后按指定编码转UTF-8。

PHP 读取 RTF 文件时出现乱码,根本原因不是 PHP 自身不支持 RTF,而是 RTF 是一种带控制指令的富文本格式,**本身不等于纯文本,且内部编码声明(如 \ansicpg936、\uc1)和实际字节流需手动解析转换**。直接用 file_get_contents() 或 fopen() 读取原始字节后,若未按 RTF 头部指定的代码页解码,就会显示错乱。
看清 RTF 文件的实际编码声明
RTF 文件开头通常含类似 {\rtf1\ansi\ansicpg936\deff0... 的控制字。\ansicpg936 表示该文档使用 Windows 简体中文代码页(GBK),\ansicpg1252 是西欧 Windows 编码,\uc1 表示 Unicode 字符占 2 字节。你必须先提取这个值,再决定如何解码。
- 用正则匹配:
preg_match('/\\\\ansicpg(\d+)/', $rtfContent, $matches)获取代码页数字 - 常见代码页对照:936→GBK,1252→Windows-1252,65001→UTF-8(但 RTF 中极少直接用 UTF-8)
- 注意:有些 RTF 用
\uc1+ Unicode 十六进制(如'\u8749'),需额外处理 Unicode 转义
剥离 RTF 控制指令,只提取可见文本
不能把整个 RTF 当作纯文本 decode——里面大量 \b\i\par\f0\fs24 等命令会干扰解码。需先做“去标记”处理:
- 简单方案:用开源库 rtf-html-php(轻量,专注转 HTML/文本),它自动识别 \ansicpg 并执行对应解码
- 手动精简:用正则清除所有反斜杠命令(
/\\\\[a-zA-Z]+(?:-?\d+)?/)和花括号(保留内容层级需谨慎),再清理多余空格和控制字符(如 \tab、\par) - 关键点:去除指令后,剩余字节才可按 \ansicpg 指定编码转为 UTF-8 供 PHP 使用
正确执行编码转换(以 GBK → UTF-8 为例)
获取到原始文本字节(已去指令)和代码页(如 936)后,调用 iconv 或 mb_convert_encoding:
立即学习“PHP免费学习笔记(深入)”;
$utf8Text = iconv('GBK', 'UTF-8//IGNORE', $plainBytes);- 或更稳妥:
$utf8Text = mb_convert_encoding($plainBytes, 'UTF-8', 'GBK'); - 若代码页是 1252:
mb_convert_encoding($plainBytes, 'UTF-8', 'Windows-1252') - 遇到无效字节加
//IGNORE或//TRANSLIT防止报错中断
特殊情况:含 Unicode 转义(\uXXXX)的 RTF
当 RTF 含 \u8749? 类似结构(问号是占位符),说明用了 Unicode 字符。需单独提取十六进制部分并转为 UTF-8:
- 匹配:
preg_replace_callback('/\\\\u([0-9a-fA-F]{4})/', function($m) { return mb_convert_encoding(pack('n', hexdec($m[1])), 'UTF-8', 'UTF-16BE'); }, $text) - 注意:RTF 中 \u 后跟的是 UTF-16BE 码点,需先 pack 成双字节,再用 UTF-16BE 解码为 UTF-8
- 该步骤应在去指令之后、主编码转换之前完成,避免混淆
不复杂但容易忽略:RTF 不是编码问题,是格式解析问题。先认代码页,再清指令,最后转码,三步缺一不可。别试图用 file_get_contents 直接 echo ——那只是在打印一堆控制符和错码字节。











