求指导:如何用PHP实现类似维基百科的Markdown层级列表解析器
我来帮你拆解这个问题的实现逻辑,其实核心就是标题层级解析+栈式嵌套管理,一步步来:
1. 先明确规则:标题层级与等号的对应关系
从你的例子能看出来,等号的数量直接对应标题的嵌套层级:
===aaa===→ 1级标题(最外层列表项)====bbb====→ 2级标题(1级的子项)=====eee=====→ 3级标题(2级的子项)
简单说就是:层级数 = 两侧等号的最小数量 - 2(比如3个等号→3-2=1级,4个→4-2=2级)。如果遇到两侧等号数量不一致的情况(比如你例子里的===ccc====),取开头或结尾的最小等数来计算层级就好。
2. 核心实现思路:用栈跟踪嵌套结构
嵌套列表的难点在于处理层级的切换——什么时候开新的子列表,什么时候回到父列表。用栈来管理当前的层级上下文是最顺手的方式,逻辑如下:
- 初始化一个空栈,用来记录当前所在的层级
- 遍历每个解析后的标题(层级+内容):
- 回退到父层级:如果当前标题的层级比栈顶的层级低,就不断弹出栈顶元素,同时闭合对应的子列表标签,直到栈顶层级小于当前层级。
- 添加列表项:
- 如果栈为空,说明是顶级标题,直接在最外层
<ol>里加<li> - 如果栈顶层级+1等于当前层级,说明是父项的直接子项,先开一个子
<ol>,再加<li>
- 如果栈为空,说明是顶级标题,直接在最外层
- 记录当前层级:把当前层级压入栈,继续处理下一个标题
- 遍历结束后,闭合所有未关闭的列表标签
3. 代码示例(Python)
先写一个标题解析函数,把每行文本转成(层级,内容)的格式:
def parse_title(line): line = line.strip() # 判断是否是标题行(首尾都是等号) if not (line.startswith('=') and line.endswith('=')): return None # 统计左侧等号数量 left_eq_count = 0 while left_eq_count < len(line) and line[left_eq_count] == '=': left_eq_count += 1 # 统计右侧等号数量 right_eq_count = 0 while right_eq_count < len(line) and line[-1 - right_eq_count] == '=': right_eq_count += 1 # 计算层级:取最小等号数-2 level = min(left_eq_count, right_eq_count) - 2 # 提取标题内容(去掉首尾等号后去空格) content = line[left_eq_count:-right_eq_count].strip() return (level, content)
再写生成嵌套有序列表的函数:
def titles_to_nested_ol(titles): stack = [] html_parts = ['<ol>'] for level, text in titles: # 回退到合适的父层级 while stack and stack[-1] >= level: stack.pop() html_parts.append('</ol></li>') # 添加当前标题的列表项 if not stack: # 顶级标题 html_parts.append(f' <li>{text}') else: # 子标题,先开子列表 html_parts.append(f' <ol><li>{text}') stack.append(level) # 闭合所有剩余的标签 while stack: stack.pop() html_parts.append('</ol></li>') html_parts.append('</ol>') return '\n'.join(html_parts)
测试你的例子(修正了笔误的标题行):
input_lines = [ '===aaa===', '====bbb====', '===ccc===', '====ddd====', '=====eee=====' ] # 提取所有标题 titles = [] for line in input_lines: title_info = parse_title(line) if title_info: titles.append(title_info) # 生成HTML列表 print(titles_to_nested_ol(titles))
输出结果和你给的示例几乎一致:
<ol> <li>aaa <ol><li>bbb</ol></li> <li>ccc <ol><li>ddd <ol><li>eee</ol></li> </ol></li> </ol>
4. 边界情况处理
- 跳级标题:比如从1级直接跳到3级,你可以选择把3级当成2级处理,或者补全空的中间层级,具体看需求
- 标题下的内容:如果要把标题下面的文本也放到列表项里,只需要在解析时把标题和后续内容关联起来,生成
<li>时把内容加进去(比如<li>aaa<br>aaaaaaaaaaaaaaaaaa</li>) - 等号数量异常:比如一侧等号多一侧少,用最小等号数计算层级是最稳妥的方式
内容的提问来源于stack exchange,提问作者DolDurma
相关产品推荐
相关产品推荐

