返回 AI

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

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

        吃了 修完嵌套问题,发现

          标签和
        被拆到了不同的段落块里。 原因是:正则 [ ]* 在匹配最后一个
      • 后面的换行时,把列表后面的空行也吞进去了: ` <li>item1</li> <li>item2</li> ← 空行也被匹配了 </ul> ` 替换后变成: ` <ul><li>item1</li> <li>item2</li> </ul> `
      前面,而段落分割是按 切的。一切,
    就跑到了另一个 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 <p>这个邮箱会用来:</p> <ul style="padding-left:24px;margin:8px 0;"> <li style="margin:4px 0;">接收评估结果通知</li> <li style="margin:4px 0;">注册/登录 MiMo 开放平台</li> </ul> ` 但微信草稿箱里还是多了一个空 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("<ul>"); } listBuf.append("<li>" + trimmedLine.substring(2) + "</li>"); } else { if (inUl) { listBuf.append("</ul> "); 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日