一条空列表的 debug 之旅:我修了 4 次才搞定微信公众号排版

admin 📖 2 分钟阅读
# 一条空列表的 debug 之旅:我修了 4 次才搞定微信公众号排版 ## 事情是这样的 我在做一个自动把博客文章推送到微信公众号草稿箱的功能。后端拿到 Markdown,转成 HTML,再调微信 API 推进去。 文章内容是小米 MiMo 的活动介绍,里面有一段列表: ``` 这个邮箱会用来: - 接收评估结果通知 - 注册/登录 MiMo 开放平台 ``` 很简单对吧?两行列表。 结果推到微信草稿箱一看—— **中间多了一个空的圆点。** 就是:`• 接收评估结果通知`,然后一个光秃秃的 `•`,然后 `• 注册/登录 MiMo 开放平台`。 三行 bullet,中间那个啥也没有。 ## 第一次修:正则匹配出了问题 我第一反应是:源 markdown 有问题?可能有空行? 查了数据库,源内容干干净净,没有多余空行。 那就看 Markdown → HTML 的转换逻辑。原来的代码是用正则做的: ```java // 把 "- item" 转成
  • html = html.replaceAll("(?m)^- (.+)$", "
  • $1
  • "); // 把连续的
  • 包进
      html = html.replaceAll("((?:]*>.*[\n\r]*)+)", "
        $1
      "); ``` 然后还有有序列表的处理: ```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("` 标签本身,不包含外层的 `
                ` 标签。 结果:`
                  1. item
                `,双重嵌套。 **修复:** 给无序列表的 `
              • ` 加 `type="disc"`,有序列表加 `type="1"`,正则只匹配对应类型。 ## 第二次修:`
              ` 被 `

              ` 吃了 修完嵌套问题,发现 `

                ` 标签和 `
              ` 被拆到了不同的段落块里。 原因是:正则 `[ ]*` 在匹配最后一个 `` 后面的换行时,把列表后面的**空行**也吞进去了: ```
            • item1
            • item2
            • ← 空行也被匹配了
            ``` 替换后变成: ```
            • 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

    这个邮箱会用来:

    • 接收评估结果通知
    • 注册/登录 MiMo 开放平台
    ``` **但微信草稿箱里还是多了一个空 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,到微信草稿箱还是有问题。有些坑只有实际部署才能发现。 --- *凌晨两点,一个空圆点,四次修复。做公众号的第三天。*
  • 🤖 本文内容由AI辅助整理生成,仅供参考
    ← 上一篇 小米放大招:百万亿Token免费送,手把手教你白嫖MiMo大模型 下一篇 → AI 前沿速递 | 2026年05月07日