# 一条空列表的 debug 之旅:我修了 4 次才搞定微信公众号排版
## 事情是这样的
我在做一个自动把博客文章推送到微信公众号草稿箱的功能。后端拿到 Markdown,转成 HTML,再调微信 API 推进去。
文章内容是小米 MiMo 的活动介绍,里面有一段列表:
```
这个邮箱会用来:
- 接收评估结果通知
- 注册/登录 MiMo 开放平台
```
很简单对吧?两行列表。
结果推到微信草稿箱一看——
**中间多了一个空的圆点。**
就是:`• 接收评估结果通知`,然后一个光秃秃的 `•`,然后 `• 注册/登录 MiMo 开放平台`。
三行 bullet,中间那个啥也没有。
## 第一次修:正则匹配出了问题
我第一反应是:源 markdown 有问题?可能有空行?
查了数据库,源内容干干净净,没有多余空行。
那就看 Markdown → HTML 的转换逻辑。原来的代码是用正则做的:
```java
// 把 "- item" 转成
html = html.replaceAll("(?m)^- (.+)$", "$1");
// 把连续的
包进
html = html.replaceAll("((?:- ]*>.*
[\n\r]*)+)", "");
```
然后还有有序列表的处理:
```java
// 把 "1. item" 转成 -
html = html.replaceAll("(?m)^\d+\.\s+(.+)$", "
- $1
");
// 把连续的 - 包进
html = orderedGroup.matcher(html).replaceAll(mr -> {
String m = mr.group();
if (m.contains(" 里的就跳过
return "" + m + "
";
});
```
**问题 1:`` 嵌套在 `` 里。**
有序列表的分组正则会匹配所有 `- ` 标签——包括已经在 `
` 里的。`m.contains("` 标签本身,不包含外层的 `` 标签。
结果:``,双重嵌套。
**修复:** 给无序列表的 `- ` 加 `type="disc"`,有序列表加 `type="1"`,正则只匹配对应类型。
## 第二次修:`
` 被 `` 吃了
修完嵌套问题,发现 `
` 被拆到了不同的段落块里。
原因是:正则 `[
]*` 在匹配最后一个 `
` 后面的换行时,把列表后面的**空行**也吞进去了:
```
- item1
- item2
← 空行也被匹配了
```
替换后变成:
```
```
`
` 在 `
` 前面,而段落分割是按 `
` 切的。一切,`
` 就跑到了另一个 block 里,被 `` 包住了。
**修复:** 加了清理步骤,把 `
` 移到 `
`。
## 第三次修:Java 正则少了个括号
修完上面两个,编译报错了:
```
java.util.regex.PatternSyntaxException: Unclosed group near index 59
```
查了半天,发现是这一行:
```java
trimmed.matches("(?s)^(?!<)(.+?)\n(<(ul|ol|pre|blockquote|table|h[1-4]|hr).*")
```
`h[1-4]` 后面少了一个 `)`。正则本身是对的,但 Java 字符串里少写了闭合括号。
**修复:** 加上 `)`。
## 第四次(最终):微信编辑器不认
以上三个都修了,逻辑上完全正确。Python 模拟跑出来的 HTML 干干净净:
```html
这个邮箱会用来:
```
**但微信草稿箱里还是多了一个空 bullet。**
我怀疑是 `type="disc"` 属性的问题——这是 HTML4 的遗留属性,微信编辑器可能不认识,处理的时候出了幺蛾子。
**最终修复:** 彻底重写列表处理。放弃正则方案,改成**逐行解析**:
```java
for (String line : lines) {
String trimmedLine = line.trim();
boolean isUlItem = trimmedLine.matches("^- .+$");
if (isUlItem) {
if (!inUl) { inUl = true; listBuf.append(""); }
listBuf.append("- " + trimmedLine.substring(2) + "
");
} else {
if (inUl) { listBuf.append("
"); inUl = false; }
listBuf.append(line + "
");
}
}
```
不再用 `type="disc"`,不再用复杂的正则嵌套。纯 `- `,干干净净。
**这次终于好了。**
## 踩坑总结
| 问题 | 原因 | 解法 |
|------|------|------|
| `
` 嵌套在 `` 里 | 有序列表正则匹配了所有 `- ` | 用 `type` 属性区分(后来弃用) |
| `
` 被 `` 包裹 | `[
]*` 吞了空行 | 清理 `
` |
| Java 编译报错 | 正则少了个 `)` | 加上 |
| 微信编辑器空 bullet | `type` 属性不兼容 | 逐行解析,不用正则 |
## 教训
**1. 微信公众号是个"特殊"的 HTML 环境。**
你以为标准 HTML 能用,其实微信编辑器对很多属性的处理和浏览器不一样。`type="disc"` 在 Chrome 没问题,到微信就炸。
**2. 用正则做结构化解析,迟早要出事。**
列表是有嵌套结构的,用正则平铺处理,处理一个层级还行,两个层级就开始互相干扰。最后改成逐行解析(本质上是个简单状态机),代码更长但逻辑清晰得多。
**3. 排版问题要实际验证。**
我用 Python 模拟跑出来"完美"的 HTML,到微信草稿箱还是有问题。有些坑只有实际部署才能发现。
---
*凌晨两点,一个空圆点,四次修复。做公众号的第三天。*