02/18/2026
설계 요약
CSV 읽기
page_number 기준으로 텍스트 병합
rule 기반 문단 분리
빈 줄
번호 패턴
헤더 패턴
SemanticBlock 객체로 저장
아래 코드는:
CSV 읽고
페이지 단위 병합
rule-based 분리
semantic 블록 리스트 반환
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.*;
import java.util.regex.Pattern;
public class SemanticChunker {
static class SemanticBlock {
int pageNumber;
String text;
public SemanticBlock(int pageNumber, String text) {
this.pageNumber = pageNumber;
this.text = text;
}
}
public static void main(String[] args) throws Exception {
Path csvPath = Paths.get("input.csv");
Map<Integer, StringBuilder> pageMap = readAndMergeByPage(csvPath);
List<SemanticBlock> blocks = ruleBasedSplit(pageMap);
System.out.println("총 semantic block 수: " + blocks.size());
}
// 1️⃣ CSV 읽고 page 기준 병합
private static Map<Integer, StringBuilder> readAndMergeByPage(Path csvPath) throws IOException {
Map<Integer, StringBuilder> pageMap = new LinkedHashMap<>();
try (BufferedReader br = Files.newBufferedReader(csvPath, StandardCharsets.UTF_8)) {
String line = br.readLine(); // header skip
while ((line = br.readLine()) != null) {
// 간단 CSV 파싱 (text에 쉼표 없다는 전제)
String[] parts = line.split(",", 3);
if (parts.length < 3) continue;
int page = Integer.parseInt(parts[0].trim());
String text = parts[2].replace("\"", "").trim();
pageMap
.computeIfAbsent(page, k -> new StringBuilder())
.append(text)
.append("\n");
}
}
return pageMap;
}
// 2️⃣ rule-based 문단 분리
private static List<SemanticBlock> ruleBasedSplit(Map<Integer, StringBuilder> pageMap) {
List<SemanticBlock> result = new ArrayList<>();
Pattern headerPattern = Pattern.compile(
"^(제\\d+장|\\d+\\.\\s|[IVX]+\\.\\s|[①-⑳]|-\\s).*"
);
for (Map.Entry<Integer, StringBuilder> entry : pageMap.entrySet()) {
int page = entry.getKey();
String pageText = entry.getValue().toString();
String[] lines = pageText.split("\\r?\\n");
StringBuilder currentBlock = new StringBuilder();
for (String line : lines) {
line = line.trim();
if (line.isEmpty()) {
// 빈 줄 → block 종료
if (currentBlock.length() > 0) {
result.add(new SemanticBlock(page, currentBlock.toString().trim()));
currentBlock.setLength(0);
}
continue;
}
// 헤더 패턴이면 강제 분리
if (headerPattern.matcher(line).matches()) {
if (currentBlock.length() > 0) {
result.add(new SemanticBlock(page, currentBlock.toString().trim()));
currentBlock.setLength(0);
}
result.add(new SemanticBlock(page, line));
} else {
currentBlock.append(line).append(" ");
}
}
// 페이지 끝 처리
if (currentBlock.length() > 0) {
result.add(new SemanticBlock(page, currentBlock.toString().trim()));
}
}
return result;
}
}
이 코드가 하는 일
- CSV를 page별로 다시 묶는다
- 줄바꿈 기준 분리
- 헤더/번호 패턴 강제 분리
- semantic block 리스트 생성
OCR JSON
↓
Flatten CSV
↓
Rule-based CSV (block_id, page_number, element_ids, merged_text)
↓
Token-safe chunk CSV ← 지금 만들 단계
↓
LLM 처리