
在 php 中操作 rtf 文件时,直接插入 utf-8 编码的波兰语字符(如 `częstochowa`)会导致乱码,因为 rtf 格式不原生支持 utf-8;需将 unicode 字符转换为 rtf 兼容的 `un?` 控制字格式,并确保文档声明正确的字符集。
RTF(Rich Text format)是一种历史悠久的文档格式,其原生编码机制基于 ANSI(如 windows-1252),不直接支持 UTF-8 字节流。当 php 以 UTF-8 读取并写入 RTF 内容时,若未按 RTF 规范对 Unicode 字符进行转义,word 等编辑器会错误解析字节序列,表现为 CzÄ™stochowa 或 Czêstochowa 等乱码——这本质是编码映射失配所致。
✅ 正确做法:将 Unicode 字符转为 RTF 的 uN? 格式
RTF 2.0+ 支持 Unicode 字符嵌入,语法为:
uN?
其中 N 是该字符的 Unicode 码点十进制值(如 ę 的 Unicode 是 U+0142 → 十进制 322),? 是一个占位 ASCII 字符(通常用空格或 .,用于兼容旧解析器)。
因此,Częstochowa 中的 ę(U+0142)应写作 u322?,完整字符串需逐字符转换。
立即学习“PHP免费学习笔记(深入)”;
但手动编码繁琐且易错。推荐使用以下健壮方案:
function utf8ToRtfUnicode($utf8String) { $result = ''; for ($i = 0; $i < mb_strlen($utf8String, 'UTF-8'); $i++) { $char = mb_substr($utf8String, $i, 1, 'UTF-8'); $code = mb_ord($char, 'UTF-8'); // PHP 7.2+,获取 Unicode 码点 if ($code <= 0xFFFF) { $result .= sprintf('u%d?', $code); } else { // 处理代理对(超出 BMP 的字符,如 emoji),此处可选 $result .= $char; // 或跳过/替换 } } return $result; } // 使用示例 $content = file_get_contents('template.rtf'); // 关键:确保原始 RTF 文件头部已声明 ansicpg65001(UTF-8)或 uc1 + udff // 若无,建议在 RTF 开头插入:{rtf1ansiansicpg65001uc1udff ... $replaced = str_replace( '[company_address]', utf8ToRtfUnicode('Częstochowa'), $content ); file_put_contents('uploads/test.rtf', $replaced);
⚠️ 重要前提:RTF 文件头部必须包含 ansicpg65001(声明 UTF-8)和 uc1(表示后续 uN? 序列存在)、udff(指定 Unicode 默认字体)。否则 Word 仍按 ANSI 解析。若原始 RTF 由 Word 保存,通常已含这些标记;若为手写 RTF,请检查并补全:{rtf1ansiansicpg65001uc1udffdeff0...
❌ 为什么原答案 mb_convert_encoding('Częstochowa', 'html') 不可靠?
原回答中:
$string = str_replace('', "\u", mb_convert_encoding('Częstochowa', 'html'));
该方法依赖 HTML 实体(如 ę → ł),再替换为 u322?。但 mb_convert_encoding($str, 'HTML') 并非标准用法(PHP 文档未定义 'HTML' 为目标编码),实际行为因环境而异,不可移植、不推荐用于生产。且未处理多字节边界与 RTF 上下文,易引入额外转义错误。
✅ 最佳实践总结
- ✅ 始终以二进制模式读取 RTF(file_get_contents 默认满足);
- ✅ 使用 mb_ord() + sprintf('u%d?', $code) 生成标准 uN? 序列;
- ✅ 确保 RTF 头部含 ansicpg65001uc1udff;
- ✅ 避免 mb_convert_encoding($str, 'UTF-8') 对 RTF 内容盲目转码——RTF 是混合文本/控制字格式,整体转 UTF-8 会破坏 fonttbl 等 ANSI 结构;
- ✅ 替换后,用 Word 或 Libreoffice 验证渲染效果,而非仅看源码。
遵循以上步骤,即可稳定、准确地在 RTF 模板中注入含波兰语、捷克语等扩展拉丁字符的动态内容。