Apache POI写入Word的bidi字符串修改文件后无法正常读取问题
问题原因
你手动添加的\u202E(从右到左覆盖控制符RLO)和\u202C(弹出方向格式化控制符PDF)都属于不可见的Unicode双向控制字符,触发问题的核心逻辑如下:
- 首次写入时,控制符和业务文本被放在同一个
XWPFRun节点内,所以首次回读能拿到完整的字符串,isBlank()判断正常。 - 用Word修改文件后,Word会自动重构文档的XML结构:会把不可见控制符和可见业务文本拆分成独立的
XWPFRun节点,甚至会将你手动插入的控制符单独存到一个空Run中,可见业务文本被拆分到其他相邻Run里。 - 如果你读取时只提取了原写入位置的单个Run内容,拿到的就只剩两个不可见的控制符,Java的
String.isBlank()会将这类不可见格式控制符判定为空白字符,因此直接返回true。
可行解决方案
- 方案1:改用POI原生API设置双向属性,不要手动插入控制符
对需要从右到左显示的文本,直接给对应的XWPFRun设置RTL属性:
这种方式的标记是Word原生支持的属性,修改后不会被拆分,读写都不会出现内容丢失的问题。XWPFRun run = paragraph.createRun(); run.setText(aString); run.setRtl(true); // 底层自动生成Word兼容的RTL标记,不会被拆分 - 方案2:读取时拼接相邻Run的全部内容
不要依赖单个XWPFRun的文本内容,读取时遍历目标段落的所有Run,把所有文本拼接完成后再做后续判断和业务处理。 - 方案3:自定义非空判断逻辑
如果必须保留手动插入控制符的逻辑,判断空之前先过滤掉所有双向控制类字符,再判断剩余内容是否为空:// 过滤所有双向控制符的示例逻辑 public static boolean isBidiStringBlank(String str) { if (str == null) return true; return str.codePoints() .filter(c -> !((c >= 0x202A && c <= 0x202E) || (c >= 0x2066 && c <= 0x2069))) .allMatch(Character::isWhitespace); }
内容的提问来源于stack exchange,提问作者user12616222
相关产品推荐
相关产品推荐

