import os, sys, re sys.stdout.reconfigure(encoding='utf-8') target_files = [ # 02_토목부문 'resources/knowledge/original/원가계산/건설공사_표준품셈/02_토목부문/제1장_도로포장공사.md', 'resources/knowledge/original/원가계산/건설공사_표준품셈/02_토목부문/제2장_하천공사.md', 'resources/knowledge/original/원가계산/건설공사_표준품셈/02_토목부문/제3장_터널공사.md', 'resources/knowledge/original/원가계산/건설공사_표준품셈/02_토목부문/제4장_궤도공사.md', 'resources/knowledge/original/원가계산/건설공사_표준품셈/02_토목부문/제5장_강구조공사.md', 'resources/knowledge/original/원가계산/건설공사_표준품셈/02_토목부문/제6장_관부설및접합공사.md', 'resources/knowledge/original/원가계산/건설공사_표준품셈/02_토목부문/제7장_항만공사.md', 'resources/knowledge/original/원가계산/건설공사_표준품셈/02_토목부문/제8장_지반조사.md', 'resources/knowledge/original/원가계산/건설공사_표준품셈/02_토목부문/제9장_측량.md', # 05_유지관리부문 'resources/knowledge/original/원가계산/건설공사_표준품셈/05_유지관리부문/제1장_공통.md', 'resources/knowledge/original/원가계산/건설공사_표준품셈/05_유지관리부문/제2장_토목.md', 'resources/knowledge/original/원가계산/건설공사_표준품셈/05_유지관리부문/제3장_건축.md', 'resources/knowledge/original/원가계산/건설공사_표준품셈/05_유지관리부문/제4장_기계설비.md', # 04_기계설비부문 제13장 'resources/knowledge/original/원가계산/건설공사_표준품셈/04_기계설비부문/제13장_플랜트설비공사.md', ] print("=== Scanning for Page Boundary Duplicate/Overlapped Rows ===") page_markers = [ r'^\s*→\s*\d+', r'^\s*\d+\s*[가-힣]+부문', r'^\s*[가-힣]+부문\s*\d+', r'^\s*제\d+장\s*[가-힣]+\s*\d+', r'^\s*\d+\s*제\d+장', r'^\s*→\s*$', r'^\s*→[가-힣]+부문\d+', ] page_regex = re.compile('|'.join(page_markers)) boundary_cases = [] for fpath in target_files: fname = os.path.basename(fpath) with open(fpath, 'r', encoding='utf-8') as f: lines = f.readlines() for idx, line in enumerate(lines): line_str = line.strip() if page_regex.search(line_str): l_no = idx + 1 # Found page boundary at l_no # Look at 30 lines before and 30 lines after before_lines = [lines[i].strip() for i in range(max(0, idx-30), idx) if lines[i].strip().startswith('|')] after_lines = [lines[i].strip() for i in range(idx+1, min(len(lines), idx+31)) if lines[i].strip().startswith('|')] # Filter out divider lines before_data = [l for l in before_lines if not all(re.match(r'^:?-+:?$', c) for c in [x.strip() for x in l.split('|')[1:-1]] if c)] after_data = [l for l in after_lines if not all(re.match(r'^:?-+:?$', c) for c in [x.strip() for x in l.split('|')[1:-1]] if c)] if not before_data or not after_data: continue # Check for duplicate rows or squashed rows between before and after for b_idx, bl in enumerate(before_data): b_cells = [c.strip() for c in bl.split('|')[1:-1]] b_nums = set(re.findall(r'\b\d+(?:\.\d+)?\b', bl)) b_words = set(re.findall(r'[가-힣A-Za-z0-9]+', bl)) for a_idx, al in enumerate(after_data): a_cells = [c.strip() for c in al.split('|')[1:-1]] # 1. Exact match if b_cells == a_cells and len([c for c in b_cells if c not in ["", "-", "〃"]]) >= 2: boundary_cases.append({ 'file': fname, 'boundary_line': l_no, 'marker': line_str, 'type': 'exact_match_across_boundary', 'before_row': bl[:80], 'after_row': al[:80] }) # 2. After row contains multiple before rows (squashed overlap) a_words = set(re.findall(r'[가-힣A-Za-z0-9]+', al)) a_nums = set(re.findall(r'\b\d+(?:\.\d+)?\b', al)) if len(b_nums) >= 2 and b_nums.issubset(a_nums) and len(b_words.intersection(a_words)) >= 2: boundary_cases.append({ 'file': fname, 'boundary_line': l_no, 'marker': line_str, 'type': 'squashed_overlap_across_boundary', 'before_row': bl[:80], 'after_row': al[:80] }) print(f"Total boundary cases found: {len(boundary_cases)}") for bc in boundary_cases: print(f"[{bc['file']}:L{bc['boundary_line']}] {bc['marker']} - {bc['type']}") print(f" Before: {bc['before_row']}") print(f" After: {bc['after_row']}")