# -*- coding: utf-8 -*- """건설품셈 공통 제3장 md 읽기 · 표 만들기 · 쓰기 도구 (build_소요량_건설품셈_공통3장.py 가 씀).""" import json import re from pathlib import Path ROOT = Path(__file__).resolve().parents[3] SRC = ( ROOT / "resources/knowledge/original/원가계산/건설공사_표준품셈/본문/01_공통부문/제03장_토공사" ) OUT = ROOT / "resources/master_data/소요량_건설품셈_공통3장.json" DIV = "건설품셈 공통 " class Raw(str): """원문 수 글자 그대로(0.040 유지).""" NUMRE = re.compile(r"^\d[\d,]*(\.\d+)?$") def N(x): x = x.strip() if not NUMRE.match(x): raise ValueError(f"수 아님: {x!r}") return Raw(x.replace(",", "")) def R(x): return Raw(x.replace(",", "")) def join(cell): parts = [p.strip() for p in cell.split("
")] out = parts[0] for p in parts[1:]: out += ("" if out.endswith("∼") else " ") + p return out def unesc(s): return re.sub(r"\\([\\*\-#>.|])", r"\1", s).replace("
", " ") # ── md 읽기 ──────────────────────────────────────────────── class Tab: def __init__(self, rows, base): self.rows, self.base = rows, base def body_lines(path): lines = path.read_text(encoding="utf-8").split("\n") at = [i for i, x in enumerate(lines) if x.strip() == "---"] return lines[at[1] + 1 :] def parse_tables(path): L = body_lines(path) out, i = [], 0 while i < len(L): if not L[i].startswith("|"): i += 1 continue j = i while j < len(L) and L[j].startswith("|"): j += 1 rows = [] for ln in L[i:j]: cells = [c.strip() for c in ln.strip().strip("|").split("|")] if all(re.fullmatch(r":?-+:?", c) for c in cells): continue rows.append(cells) k = i - 1 while k >= 0 and not L[k].strip(): k -= 1 m = re.fullmatch(r"\((.*)\)", L[k].strip()) if k >= 0 else None out.append(Tab(rows, m.group(1) if m else "")) i = j return out FILES = {p.name[:4]: p for p in sorted(SRC.glob("3-*.md"))} TABS = {k: parse_tables(p) for k, p in FILES.items()} def sec_lines(ident): """항 번호(3-2-1)의 본문 줄(제목 포함, 같은·윗 제목 전까지).""" L = body_lines(FILES[f"3-{int(ident.split('-')[1]):02d}"]) for at, ln in enumerate(L): m = re.match(rf"^(#+)\s*{re.escape(ident)}(\s|\(|$)", ln) if m: end = len(L) for k in range(at + 1, len(L)): h = re.match(r"^(#+)\s", L[k]) if h and len(h.group(1)) <= len(m.group(1)): end = k break return L[at:end] raise KeyError(ident) def sec_text(ident): return unesc("\n".join(sec_lines(ident))) def notes(ident, n=0): """항의 n번째 [주] 묶음 줄들(원문 글).""" blocks, cur = [], None for ln in sec_lines(ident): s = ln.strip() if s == "[주]": cur = [] blocks.append(cur) continue if cur is None: continue if re.match(r"^#", s) or s.startswith("[참고") or re.match(r"^[가-힣]\. ", s): cur = None continue if not s or s.startswith(("|", "