Files
ChildHood/tools/level-builder/parsers/markdown_table.py
T
starmen1265 a3c38aa92a fix(level-builder): 锚点补偿+旋转修复+天空盒+解析器去重
- asset_mapping: 38种BSP资产实测锚点+pivot_offset_xy自动补偿
- scene_builder: Rotator改关键字参数修正旋转轴映射
- scene_builder: spawn前XY锚点补偿(支持Yaw旋转)
- scene_builder: 新增ExponentialHeightFog+大气效果后处理
- build_cmdline: 构建后自动添加雾+GameMode+DirectionalLight大气
- build_guide_parser: 去重+全局地面+旋转列名兼容+边界段范围修正
- BarrierWall实际尺寸修正(60×210非20×100)
- 搭建指南: 全局地面中心坐标+西边界旋转统一+院门改双开门
2026-06-08 19:05:50 +08:00

193 lines
5.5 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)' 为三元组(度数)。
处理箭头注释格式:'(0,0,0) → Yaw=90°' 只取括号内部分。
"""
if not text:
return (0.0, 0.0, 0.0)
# 先尝试提取括号内的旋转值,忽略箭头后面的注释
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()