Files
ChildHood/tools/level-builder/parsers/markdown_table.py
T
starmen1265 0f96b79dba feat(tools): 白模关卡自动生成Python脚本——Level Builder
两阶段流水线:搭建指南Markdown → JSON → UE5 Actor
- 解析器:4种表格变体+范围展开+坐标表达式计算
- 配置:37种BSP资产映射+13种材质+7区域定义
- 验证:265个Actor全部通过,0错误
- Builder:UE5 Python API自动放置Actor+材质+Outliner
- 报告:自动生成搭建报告Markdown

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 23:03:45 +08:00

187 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""通用 Markdown 管道表格解析器。
将 Markdown 表格文本解析为 list[dict],每个字典代表一行数据。
支持中文列名,处理列数不一致的情况。
"""
import re
from typing import Optional
def parse_table(lines: list[str]) -> list[dict]:
"""解析单个 Markdown 表格。
Args:
lines: 表格的文本行列表(包含表头、分隔行和数据行)。
Returns:
列表,每个元素是一行数据的字典,key 为表头文本。
返回空列表如果输入不是有效表格。
"""
if not lines:
return []
# 找到分隔行(包含 |---| 的行)
sep_idx = -1
for i, line in enumerate(lines):
if re.match(r'\s*\|[\s\-:]+\|', line):
sep_idx = i
break
if sep_idx < 1:
return [] # 没有有效的表头+分隔行
# 提取表头
header_line = lines[sep_idx - 1]
headers = _split_row(header_line)
if not headers:
return []
# 提取数据行(分隔行之后的所有 | 开头的行)
data_rows = []
for line in lines[sep_idx + 1:]:
stripped = line.strip()
if not stripped or not stripped.startswith('|'):
break # 表格结束
cells = _split_row(stripped)
data_rows.append(cells)
# 构建结果
result = []
for row in data_rows:
entry = {}
for j, header in enumerate(headers):
entry[header] = row[j] if j < len(row) else ""
result.append(entry)
return result
def parse_all_tables(text: str) -> list[tuple[list[str], list[dict]]]:
"""解析文本中的所有 Markdown 表格。
Args:
text: 完整的 Markdown 文本。
Returns:
列表,每个元素为 (header, rows) 元组。
header 为表头列表,rows 为 list[dict]。
"""
results = []
lines = text.split('\n')
i = 0
while i < len(lines):
# 寻找潜在的表格起始行(包含 | 的行)
if '|' in lines[i]:
# 收集连续的表格行
table_lines = []
while i < len(lines) and '|' in lines[i]:
table_lines.append(lines[i].strip())
i += 1
# 尝试解析
rows = parse_table(table_lines)
if rows:
# 提取表头
for tl in table_lines:
if re.match(r'\s*\|[\s\-:]+\|', tl):
sep_idx = table_lines.index(tl)
if sep_idx > 0:
headers = _split_row(table_lines[sep_idx - 1])
results.append((headers, rows))
break
continue
i += 1
return results
def find_tables_in_section(text: str, section_start: int, section_end: int) -> list[list[dict]]:
"""在文本的指定范围内找到所有表格。
Args:
text: 完整文本。
section_start: 区域起始行号。
section_end: 区域结束行号。
Returns:
表格列表,每个表格是 list[dict]。
"""
lines = text.split('\n')
section_lines = lines[section_start:section_end]
tables = []
i = 0
while i < len(section_lines):
if '|' in section_lines[i]:
# 收集连续表格行
table_lines = []
while i < len(section_lines) and '|' in section_lines[i]:
table_lines.append(section_lines[i])
i += 1
rows = parse_table(table_lines)
if rows:
tables.append(rows)
continue
i += 1
return tables
def _split_row(line: str) -> list[str]:
"""将 | 分隔的行拆分为单元格列表,去除首尾空格。"""
# 去除首尾空白
line = line.strip()
# 按管道符分割
if not line.startswith('|'):
return []
if not line.endswith('|'):
# 有些行末尾可能没有 |
parts = line.split('|')
else:
parts = line.split('|')
# 去除首尾空元素(管道符分割产生的)
cells = [c.strip() for c in parts]
# 去掉首尾空字符串
while cells and cells[0] == '':
cells.pop(0)
while cells and cells[-1] == '':
cells.pop()
return cells
def parse_location(text: str) -> tuple[float, float, float]:
"""解析位置字符串 '(X,Y,Z)' 为三元组。"""
if not text:
return (0.0, 0.0, 0.0)
m = re.search(r'\(([-\d.]+)\s*[,,]\s*([-\d.]+)\s*[,,]\s*([-\d.]+)\)', text)
if m:
return (float(m.group(1)), float(m.group(2)), float(m.group(3)))
return (0.0, 0.0, 0.0)
def parse_rotation(text: str) -> tuple[float, float, float]:
"""解析旋转字符串 '(P,Y,R)' 为三元组(度数)。"""
return parse_location(text)
def parse_scale(text: str) -> tuple[float, float, float]:
"""解析缩放字符串 '(X,Y,Z)' 为三元组。'—' 或空返回 (1,1,1)。"""
if not text or text.strip() in ('—', '–', '-', '无', ''):
return (1.0, 1.0, 1.0)
return parse_location(text)
def clean_cell(text: str) -> str:
"""清理单元格文本:去首尾空白、去反引号。"""
if not text:
return ""
text = text.strip()
# 去除 Markdown 反引号包裹
if text.startswith('`') and text.endswith('`'):
text = text[1:-1]
return text.strip()