P17 CSV 분할 정렬 실습, 버전 1.0
자료 조사·실제 실행일: 2026-10-08 KST

목표
12행 CSV를 4행씩 정렬한 다음 병합합니다. 시간순, 같은 시간이면
대소문자를 구별하는 ID 문자순입니다. 10은 2보다 먼저, A는 a보다 먼저입니다.
중복 ID는 전체 입력에서 거부합니다. 다른 정렬 묶음에 있어도 같습니다.
source_row는 헤더를 제외한 CSV 레코드 순번입니다. 메모의 줄바꿈 때문에
텍스트 편집기의 물리적 줄 번호와 다를 수 있습니다.

실행 전 조건
- Python 3.12 표준 라이브러리만 사용. pip 설치 없음.
- 쓰기 가능한 로컬 연습 폴더에서 수행. 원본은 따로 보관하세요.
- 출력 폴더 파일시스템이 하드 링크를 지원하고 생성 권한이 있어야 합니다.
  같은 출력 폴더에 임시 파일을 만들어 완성·검산 후 os.link로 새 이름에 연결합니다.
  기존 파일을 덮어쓰는 대체 동작은 없습니다. HARDLINK 오류면 읽기 가능한
  원본을 유지하고, 하드 링크를 지원하는 쓰기 가능한 로컬 디스크의 새
  연습 폴더로 옮겨 새 결과 이름으로 다시 실행하세요. 보안 설정을 바꾸지 마세요.
- 동기화/공유/네트워크 폴더, 다른 프로그램이 쓰는 파일은 사용하지 마세요.
- 시간은 모두 같은 시간대·같은 시계 기준의 YYYY-MM-DDTHH:MM:SS여야 합니다.
  Z, UTC 오프셋, 소수초, 애매한 날짜를 자동 해석하지 않습니다.

터미널을 이 폴더에서 열고 다음 순서로 실행하세요.
1. Python 버전 확인: python3 --version
2. 출력 폴더 만들기: mkdir output
3. 정상 실행:
   python3 external_sort.py fixtures/events.csv output/sorted.csv --chunk-rows 4 --fan-in 16
4. 자동 검산:
   python3 verify_practice.py
5. 실패·경계·모의 I/O 시험:
   python3 -m unittest test_external_sort -v

python3 명령을 못 찾으면 설치되어 있는 Python 3.12 실행 명령으로 바꾸세요.
Windows의 py -3.12 명령은 사용 가능 여부를 사용자의 환경에서 확인해야 합니다.
이 패키지는 Linux 클라우드 Python 3.12.14·SQLite 3.53.1에서 실제 실행했습니다.
Windows UI, Excel에서 다시 열기, 다른 파일시스템은 실기기 검증하지 않았습니다.

정답
- 입력 12행, 출력 12행, 수량 합계 78
- 첫 정렬 묶음 3개, 최종 병합 1회 (--chunk-rows 4 --fan-in 16)
- ID 순서: R001 R002 R003 R010 R004 R005 R006 R007 R008 R009 R011 R012
- source_row 순서: 5 3 8 2 7 10 4 12 11 1 9 6
- expected/sorted.csv는 줄바꿈 메모와 쉼표 메모를 포함한 전체 정답입니다.
- expected/summary.json에 입력·정답 SHA-256과 실행 버전이 있습니다.
- verify_practice.py의 9개 CLI 사례, unittest 27개를 모두 통과했습니다.
  각각의 테스트 안에 여러 입력을 반복하는 경우가 있으므로 실행 건수와 구별합니다.

실패를 직접 확인하기
python3 external_sort.py fixtures/duplicate_across_chunks.csv output/duplicate.csv --chunk-rows 4 --fan-in 2
-> ERROR: DUPLICATE_ID, 종료 2, 새 duplicate.csv 없음
python3 external_sort.py fixtures/bad_time.csv output/bad-time.csv --chunk-rows 4
-> ERROR: TIME_CALENDAR, 종료 2, 새 bad-time.csv 없음
python3 external_sort.py fixtures/bad_qty.csv output/bad-qty.csv --chunk-rows 4
-> ERROR: QTY, 종료 2, 새 bad-qty.csv 없음
정상 명령을 같은 output/sorted.csv로 재실행 -> OUTPUT_EXISTS, 기존 결과 보존.
이름을 바꾸고 다시 실행하세요. 파일을 지우거나 덮어쓰도록 코드를 바꾸지 마세요.

입력 계약
UTF-8 또는 UTF-8 BOM. 헤더는 time,id,qty,memo 순서가 정확해야 합니다.
id: ASCII 영숫자로 시작, 전체 1~32자, 이후 영숫자·_·-만 허용. 공백 금지.
qty: 0 또는 앞자리 0 없는 1~9자리 비음수 정수. 음수·소수·빈칸·01 거부.
memo: 10,000자 이하. 쉼표·따옴표·줄바꿈은 csv 모듈 규칙으로 보존.
빈 줄과 열 수가 다른 레코드도 거부합니다. 데이터 없는 헤더만의 파일은 0행 출력.

중간 파일과 최종 결과의 경계
실행 중 output/.p17-랜덤이름 폴더에 정렬 묶음과 검사 DB를 만듭니다.
검증/최종 연결 전 오류는 새 최종 파일을 만들지 않습니다. 정상 종료와
이번 시험의 일반 예외에서는 자신이 만든 임시 폴더만 정리합니다.
강제 종료·전원 장애·권한 변경으로 정리가 실패하면 임시 폴더가 남을 수 있습니다.
최종 연결 뒤 정리 단계에서 오류가 나면 검산된 최종 파일이 이미 있을 수 있습니다.
종료 코드만 보고 결과 파일을 덮지 말고 원본·정답과 비교하고 새 이름을 사용하세요.
정리 대상은 이번 실행이 만든 .p17-폴더뿐이며 다른 폴더를 재귀 삭제하지 않습니다.
원본 SHA-256은 읽기 전·최종 연결 전 대조하지만 파일 잠금이나 동시 수정 방지는 아닙니다.

한계
12행 실습과 19행 다단 병합 시험입니다. RAM보다 큰 파일 처리나 성능을
측정하지 않았습니다. chunk_rows는 한 정렬 묶음의 행 수이지 메모리 바이트 제한이
아닙니다. 메모 길이·파서/SQLite 캐시도 메모리를 사용합니다. fan_in은 병합 입력
스트림 수의 상한입니다. 임시 디스크에는 검사 DB와 여러 병합 단계 사본이 남아
원본보다 큰 공간이 필요합니다. 여유 공간을 확인하고 소규모 사본부터 시험하세요.
하드 링크는 완성 파일의 새 이름 공개 경계이며 fsync 기반 장애 내구성 보장이 아닙니다.
CSV는 수식처럼 보이는 메모를 변경하지 않습니다. 검산기는 텍스트로 읽으세요.
실제 스프레드시트 앱에서 열 때 자동 변환·수식 실행 방어를 보장하지 않습니다.

공식 자료
official_sources.csv 참조. 실제 시험 로그는 evidence/reader_verifier_actual.json,
evidence/all_tests.log입니다. SHA256SUMS.json은 배포 파일의 체크섬입니다.
