COBOL · 기본
데이터 처리로 배우는 COBOL
파일 쓰기와 보고서 - 머리글·상세·합계
OPEN OUTPUT·WRITE, 출력 레코드와 작업 영역 분리, 보고서 줄 레이아웃(머리글·상세·합계), 건수·합계 누적, 결과 파일을 다시 확인하기
개발자KR · 원고 갱신
이 장에서 배우는 것
앞 장에서는 거래 파일을 한 레코드(record, 파일에서 한 번에 읽고 쓰는 묶음 하나)씩 읽어 처리하는 방법을 익혔다. 읽기만 해서는 일이 끝나지 않는다. 배치 프로그램은 처리 결과를 파일로 남겨야 다음 사람이나 다음 프로그램이 그 결과를 쓸 수 있다. 이 장에서는 입금·출금 거래를 읽어 사람이 읽을 수 있는 보고서 파일 report.txt 로 쓰고, 다 쓴 뒤에 그 파일을 다시 열어 제대로 쓰였는지 확인한다.
- OPEN OUTPUT 으로 출력 파일을 열고 WRITE 로 한 줄씩 쓸 수 있다.
- 파일 구역의 출력 레코드와 작업 영역(WORKING-STORAGE)의 보고서 줄을 나누어 두는 이유를 설명할 수 있다.
- 머리글·상세·합계 세 종류의 줄 레이아웃을 칸 위치까지 맞추어 정의할 수 있다.
- 처리하는 동안 건수와 금액을 누적하고, 반복이 끝난 뒤 합계 줄로 쓸 수 있다.
- 쓴 파일을 닫고 다시 읽어 줄 수를 대조할 수 있다.
문제 상황
신용협동조합의 창구 담당자는 매일 저녁 그날의 입출금 거래를 한 장으로 받아 보고 싶어 한다. 어느 계좌에서 얼마가 들어오고 나갔는지, 입금은 몇 건에 얼마인지, 출금은 몇 건에 얼마인지가 한눈에 보여야 한다. 지금까지 만든 프로그램은 거래를 읽어 화면에 한 줄씩 표시하는 데까지만 했다. 화면 출력은 프로그램이 끝나면 사라지고, 다른 사람에게 건넬 수도 없다.
거래 파일에는 처리할 수 없는 레코드도 섞여 있다. 거래 종류 칸에 입금(D)도 출금(W)도 아닌 글자가 들어 있는 경우다. 이런 레코드를 보고서 상세 줄에 섞어 쓰면 담당자가 혼란스러우므로, 상세 줄에서는 빼고 합계 아래에 거절 건수로만 알리기로 한다.
또 하나의 걱정이 있다. 파일에 쓰는 동작은 화면에 표시하는 것과 달리 결과가 눈에 바로 보이지 않는다. 프로그램이 오류 없이 끝났어도 파일에 실제로 몇 줄이 들어갔는지는 따로 확인해야 한다. 그래서 이 장의 프로그램은 쓴 줄 수를 세어 두었다가, 파일을 다시 읽어 센 줄 수와 맞추어 본다.
입력 파일 trans.dat 의 한 줄은 24글자 고정 길이다. 계좌번호 6칸, 거래 종류 1칸(D 또는 W), 금액 7칸(숫자), 이름 10칸으로 이루어진다. 이름은 영문 대문자로 적는다. 한글 같은 여러 바이트 글자가 섞이면 칸 위치가 어긋나므로, 칸 맞춤이 중요한 이 장에서는 쓰지 않는다.
출력 파일 다루기: OPEN OUTPUT 과 WRITE
SELECT 와 FD 는 읽기와 같은 모양이다
출력 파일도 입력 파일과 똑같이 두 곳에서 선언한다. ENVIRONMENT DIVISION 의 SELECT 문이 프로그램 안의 파일 이름과 디스크의 파일 이름을 이어 주고, DATA DIVISION 의 FILE SECTION 에 있는 FD 가 그 파일의 레코드 모양을 정한다. 이 장에서는 두 파일 모두 줄 단위 텍스트 파일이므로 ORGANIZATION IS LINE SEQUENTIAL 을 쓴다. 이 방식은 한 레코드를 한 줄로 취급하고, 줄 끝의 공백은 파일에 쓸 때 잘라 낸다.
열기 방식은 세 가지다
파일을 열 때는 무엇을 하려는지 밝혀야 한다. 같은 이름의 파일이 이미 있을 때 각 방식이 어떻게 동작하는지가 특히 중요하다.
| 방식 | 할 수 있는 일 | 이미 있는 파일은 | 쓰임 |
|---|---|---|---|
| OPEN INPUT | READ | 그대로 둔다 | 거래 파일 읽기 |
| OPEN OUTPUT | WRITE | 내용을 비우고 새로 쓴다 | 매일 새 보고서 만들기 |
| OPEN EXTEND | WRITE | 끝에 이어 붙인다 | 로그 파일에 덧붙이기 |
OPEN OUTPUT 은 기존 내용을 지운다는 점을 기억해야 한다. 어제 만든 report.txt 가 있어도 오늘 실행하면 처음부터 다시 쓰이므로, 매번 같은 결과를 얻어야 하는 일괄 처리에는 알맞다. 반대로 지우면 안 되는 파일에 OUTPUT 을 쓰면 내용을 잃는다.
WRITE 는 레코드 이름을 쓴다
READ 는 파일 이름을 대상으로 하지만 WRITE 는 레코드 이름을 대상으로 한다. 이 차이는 처음에 자주 헷갈린다. 파일 이름은 "어느 파일에서 가져올지"를 가리키고, 레코드 이름은 "어떤 내용을 내보낼지"를 가리킨다고 기억하면 쉽다.
READ TRANS-FILE *> 파일 이름
WRITE REPORT-REC *> 레코드 이름
WRITE REPORT-REC FROM WS-LINE *> 작업 영역의 내용을 레코드에 옮기고 쓴다
FROM 을 붙이면 WS-LINE 의 내용이 REPORT-REC 로 복사된 뒤에 쓰인다. MOVE 한 줄과 WRITE 한 줄을 하나로 합친 것이다.
출력 레코드와 작업 영역을 나눈다
FILE SECTION 의 레코드 영역은 파일 장치와 주고받는 통로다. 읽기에서는 방금 읽은 한 줄이 놓이고, 쓰기에서는 내보낼 한 줄을 놓는 자리다. 이 영역에는 VALUE 로 초기값을 줄 수 없고, WRITE 이후에 안에 무엇이 남아 있는지도 기대하지 않는 편이 안전하다. 그래서 보고서의 실제 모양은 WORKING-STORAGE SECTION 의 작업 영역에 만들고, WRITE 직전에 한 번에 출력 레코드로 보낸다.
이렇게 나누면 얻는 것이 세 가지 있다. 첫째, 머리글 줄에는 VALUE 로 글자를 미리 넣어 둘 수 있다. 둘째, 상세 줄과 합계 줄이 한 레코드 영역을 번갈아 쓰면서 앞 줄의 찌꺼기가 남는 일을 막을 수 있다. 셋째, 줄의 모양을 바꿀 때 파일 선언은 건드리지 않아도 된다.
보고서 줄 레이아웃과 누적
세 가지 줄을 칸 단위로 설계한다
보고서는 머리글(header, 제목과 칸 이름), 상세(detail, 거래 한 건당 한 줄), 합계(total, 마지막 요약)로 이루어진다. 줄마다 모양은 다르지만 금액이 놓이는 칸은 같게 맞추어야 읽기 좋다. 이 장의 보고서는 한 줄을 40칸으로 하고 다음과 같이 나눈다.
| 칸 이름 | 위치(몇 번째 칸) | 크기 | 내용 |
|---|---|---|---|
| 계좌 | 1~6 | 6 | 계좌번호 |
| 종류 | 9~18 | 10 | DEPOSIT 또는 WITHDRAWAL |
| 금액 | 20~28 | 9 | Z,ZZZ,ZZ9 로 편집한 금액 |
| 이름 | 31~40 | 10 | 회원 이름 |
금액 칸은 앞서 출력 편집을 다룰 때 배운 Z,ZZZ,ZZ9 모양을 그대로 쓴다. 이 형식은 앞쪽 0을 공백으로 바꾸고 천 단위 쉼표를 넣어 주며, 숫자 7자리를 9칸에 담는다. 사이의 빈 칸(7~8, 19, 29~30번째)은 FILLER 로 만든다. FILLER 는 이름이 필요 없는 칸을 뜻하며 VALUE SPACES 로 공백을 채워 둔다.
합계 줄은 상세 줄과 모양이 다르지만 금액이 끝나는 위치는 28번째 칸으로 같다. 라벨 10칸, 건수 3칸, " ITEMS" 6칸, 금액 9칸을 이어 붙이면 정확히 28칸이 된다. 상세 줄의 금액이 끝나는 위치와 같으므로 위아래 금액이 나란히 보인다.
건수와 합계는 반복하는 동안 쌓는다
합계 줄에 쓸 값은 거래를 하나씩 처리할 때마다 조금씩 쌓아 둔다. 입금 건수, 입금 금액, 출금 건수, 출금 금액, 거절 건수 다섯 개가 누적 변수다. 누적 변수에는 반드시 시작값 0을 준다. 값을 주지 않으면 이전에 메모리에 있던 값이 남아 합계가 틀릴 수 있다. 이 장에서는 VALUE 0 으로 시작값을 정해 둔다.
누적 변수의 자릿수는 나올 수 있는 가장 큰 값을 담을 만큼 넉넉하게 잡는다. 금액 합계는 PIC 9(7) 로 정했으므로 9,999,999 원까지 담을 수 있다. 이 범위를 넘으면 높은 자리가 잘려 나가 합계가 틀려진다. 실제 업무에서는 하루 거래량을 가늠해 자릿수를 정한다.
한 가지 순서를 지켜야 한다. 합계 줄은 모든 거래를 읽은 뒤에 써야 한다. 반복 도중에 합계 줄을 쓰면 그때까지의 부분 합계만 나온다. 이 프로그램에서는 반복이 끝난 직후 합계 줄을 쓰는 문단을 따로 두어 이 순서를 코드 구조로 표현한다.
쓴 결과를 다시 확인하기
파일에 쓴 뒤에는 CLOSE 로 닫는다. 닫는 순간 아직 장치에 나가지 못한 내용이 파일에 반영되므로, 읽어서 확인하려면 먼저 닫아야 한다. 그다음 같은 파일을 OPEN INPUT 으로 다시 열어 끝까지 읽으면서 줄 수를 센다. 쓰면서 센 줄 수와 읽으면서 센 줄 수가 같으면 일단 줄 단위로는 이상이 없다고 볼 수 있다.
이 확인은 내용을 글자 단위로 대조하는 것이 아니다. 줄 수 대조는 가장 값싸고 가장 먼저 해 볼 만한 확인이다. 쓰기 도중 프로그램이 멈췄거나, 의도하지 않은 파일에 이어 붙였을 때 줄 수가 어긋나므로 이런 사고를 바로 알아챌 수 있다. 읽어 들인 줄은 화면에도 표시해 눈으로 보고서 모양을 점검한다. 이때 줄 끝 공백은 FUNCTION TRIM 의 TRAILING 지정으로 잘라 표시한다.
GnuCOBOL 의 파일 처리 규칙은 공식 문서에서 확인할 수 있다. GnuCOBOL 프로그래머 안내서에서 OPEN, WRITE, LINE SEQUENTIAL 항목을 찾아 보면 된다.
완성 코드
두 파일을 같은 폴더에 둔다. trans.dat 의 각 줄은 24글자이며, 마지막 칸의 이름이 10글자보다 짧으면 뒤를 공백으로 채워도 되고 비워 두어도 된다. 줄 형식 파일은 짧은 줄을 읽을 때 남는 칸을 공백으로 채워 주기 때문이다.
trans.dat
100101D0150000KIM MINSU
100102W0030000LEE JIWON
100101W0020000KIM MINSU
100203D0500000PARK HANA
100102D0075000LEE JIWON
100304W0120000CHOI DOYUN
100203X0001000PARK HANA
main.cob
IDENTIFICATION DIVISION.
PROGRAM-ID. MAIN.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT TRANS-FILE ASSIGN TO "trans.dat"
ORGANIZATION IS LINE SEQUENTIAL.
SELECT REPORT-FILE ASSIGN TO "report.txt"
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD TRANS-FILE.
01 TRANS-REC.
05 TR-ACCT PIC X(6).
05 TR-TYPE PIC X(1).
05 TR-AMT PIC 9(7).
05 TR-NAME PIC X(10).
FD REPORT-FILE.
01 REPORT-REC PIC X(40).
WORKING-STORAGE SECTION.
01 WS-SWITCHES.
05 WS-TRANS-EOF PIC X VALUE "N".
88 TRANS-AT-END VALUE "Y".
05 WS-CHECK-EOF PIC X VALUE "N".
88 CHECK-AT-END VALUE "Y".
01 WS-COUNTERS.
05 WS-DEP-CNT PIC 9(3) VALUE 0.
05 WS-DEP-SUM PIC 9(7) VALUE 0.
05 WS-WDR-CNT PIC 9(3) VALUE 0.
05 WS-WDR-SUM PIC 9(7) VALUE 0.
05 WS-REJ-CNT PIC 9(3) VALUE 0.
05 WS-LINES-OUT PIC 99 VALUE 0.
05 WS-LINES-IN PIC 99 VALUE 0.
01 WS-PRINT-LINE PIC X(40).
01 HD-TITLE PIC X(40)
VALUE "DAILY TRANSACTION REPORT".
01 HD-COLUMNS.
05 FILLER PIC X(8) VALUE "ACCT".
05 FILLER PIC X(14) VALUE "TYPE".
05 FILLER PIC X(8) VALUE "AMOUNT".
05 FILLER PIC X(10) VALUE "NAME".
01 HD-RULE PIC X(40) VALUE ALL "-".
01 DL-LINE.
05 DL-ACCT PIC X(6).
05 FILLER PIC X(2) VALUE SPACES.
05 DL-TYPE PIC X(10).
05 FILLER PIC X(1) VALUE SPACES.
05 DL-AMT PIC Z,ZZZ,ZZ9.
05 FILLER PIC X(2) VALUE SPACES.
05 DL-NAME PIC X(10).
01 TL-LINE.
05 TL-LABEL PIC X(10).
05 TL-COUNT PIC ZZ9.
05 FILLER PIC X(6) VALUE " ITEMS".
05 TL-AMT PIC Z,ZZZ,ZZ9.
01 RJ-LINE.
05 FILLER PIC X(10) VALUE "REJECTED".
05 RJ-COUNT PIC ZZ9.
05 FILLER PIC X(6) VALUE " ITEMS".
PROCEDURE DIVISION.
000-MAIN.
PERFORM 100-WRITE-REPORT
PERFORM 200-VERIFY-REPORT
STOP RUN.
100-WRITE-REPORT.
OPEN INPUT TRANS-FILE
OPEN OUTPUT REPORT-FILE
PERFORM 110-WRITE-HEADING
PERFORM 120-READ-TRANS
PERFORM UNTIL TRANS-AT-END
PERFORM 130-PROCESS-TRANS
PERFORM 120-READ-TRANS
END-PERFORM
PERFORM 140-WRITE-TOTALS
CLOSE TRANS-FILE
CLOSE REPORT-FILE.
110-WRITE-HEADING.
MOVE HD-TITLE TO WS-PRINT-LINE
PERFORM 800-WRITE-LINE
MOVE HD-COLUMNS TO WS-PRINT-LINE
PERFORM 800-WRITE-LINE
MOVE HD-RULE TO WS-PRINT-LINE
PERFORM 800-WRITE-LINE.
120-READ-TRANS.
READ TRANS-FILE
AT END SET TRANS-AT-END TO TRUE
END-READ.
130-PROCESS-TRANS.
EVALUATE TR-TYPE
WHEN "D"
ADD 1 TO WS-DEP-CNT
ADD TR-AMT TO WS-DEP-SUM
MOVE "DEPOSIT" TO DL-TYPE
PERFORM 135-WRITE-DETAIL
WHEN "W"
ADD 1 TO WS-WDR-CNT
ADD TR-AMT TO WS-WDR-SUM
MOVE "WITHDRAWAL" TO DL-TYPE
PERFORM 135-WRITE-DETAIL
WHEN OTHER
ADD 1 TO WS-REJ-CNT
END-EVALUATE.
135-WRITE-DETAIL.
MOVE TR-ACCT TO DL-ACCT
MOVE TR-AMT TO DL-AMT
MOVE TR-NAME TO DL-NAME
MOVE DL-LINE TO WS-PRINT-LINE
PERFORM 800-WRITE-LINE.
140-WRITE-TOTALS.
MOVE HD-RULE TO WS-PRINT-LINE
PERFORM 800-WRITE-LINE
MOVE "DEPOSIT" TO TL-LABEL
MOVE WS-DEP-CNT TO TL-COUNT
MOVE WS-DEP-SUM TO TL-AMT
MOVE TL-LINE TO WS-PRINT-LINE
PERFORM 800-WRITE-LINE
MOVE "WITHDRAWAL" TO TL-LABEL
MOVE WS-WDR-CNT TO TL-COUNT
MOVE WS-WDR-SUM TO TL-AMT
MOVE TL-LINE TO WS-PRINT-LINE
PERFORM 800-WRITE-LINE
MOVE WS-REJ-CNT TO RJ-COUNT
MOVE RJ-LINE TO WS-PRINT-LINE
PERFORM 800-WRITE-LINE.
200-VERIFY-REPORT.
OPEN INPUT REPORT-FILE
PERFORM UNTIL CHECK-AT-END
READ REPORT-FILE
AT END
SET CHECK-AT-END TO TRUE
NOT AT END
ADD 1 TO WS-LINES-IN
DISPLAY FUNCTION TRIM(REPORT-REC TRAILING)
END-READ
END-PERFORM
CLOSE REPORT-FILE
DISPLAY "LINES WRITTEN: " WS-LINES-OUT
DISPLAY "LINES READ BACK: " WS-LINES-IN
IF WS-LINES-OUT = WS-LINES-IN
DISPLAY "CHECK: OK"
ELSE
DISPLAY "CHECK: MISMATCH"
END-IF.
800-WRITE-LINE.
WRITE REPORT-REC FROM WS-PRINT-LINE
ADD 1 TO WS-LINES-OUT.
줄별 해설
SELECT 와 FD
SELECT 두 개는 프로그램 안의 이름 TRANS-FILE, REPORT-FILE 을 디스크의 trans.dat, report.txt 에 이어 준다. 실행하는 폴더에 trans.dat 가 있어야 하고, report.txt 는 없으면 만들어진다. TRANS-REC 는 24칸으로 입력 한 줄과 맞아떨어진다. 출력 쪽 REPORT-REC 는 PIC X(40) 한 칸짜리다. 칸 나누기는 모두 작업 영역이 맡고, 이 레코드는 40칸 글자 덩어리를 받아 파일로 보내는 역할만 한다.
작업 영역의 보고서 줄
WS-SWITCHES 의 88 레벨 TRANS-AT-END, CHECK-AT-END 는 "입력이 끝났다"는 사실에 붙인 이름이다. 읽기용과 확인용 스위치를 따로 둔 이유는, 같은 스위치를 쓰면 첫 번째 반복이 끝난 뒤 "끝남" 상태로 남아 두 번째 반복이 시작도 못 하기 때문이다. WS-COUNTERS 에는 누적 변수와 쓴 줄 수, 읽은 줄 수가 들어 있다.
HD-TITLE, HD-COLUMNS, HD-RULE 은 머리글 줄이다. HD-COLUMNS 는 8칸 "ACCT", 14칸 "TYPE", 8칸 "AMOUNT", 10칸 "NAME" 을 이어 붙인다. VALUE 로 준 글자가 칸보다 짧으면 오른쪽이 공백으로 채워지므로 "TYPE" 은 14칸을 차지하고, 그래서 AMOUNT 가 23번째 칸에서 시작해 상세 줄의 금액 끝(28번째 칸)에 맞는다. DL-LINE 은 6+2+10+1+9+2+10 으로 40칸, TL-LINE 은 10+3+6+9 로 28칸이다. RJ-LINE 의 FILLER "REJECTED" 는 라벨 칸을 10칸으로 채운다.
프로시저 부분
000-MAIN 은 보고서를 쓰는 일과 확인하는 일, 두 가지를 차례로 부르기만 한다. 100-WRITE-REPORT 는 입력 파일과 출력 파일을 함께 열고, 머리글을 쓰고, 첫 레코드를 읽은 뒤 "읽기, 처리하기"를 파일 끝까지 되풀이한다. 앞 장에서 본 "먼저 읽고, 처리하고, 또 읽는" 모양 그대로다. 반복이 끝나면 합계를 쓰고 두 파일을 닫는다.
130-PROCESS-TRANS 는 거래 종류에 따라 EVALUATE 로 갈린다. D 이면 입금 건수에 1을 더하고 금액을 입금 합계에 더한 뒤 DL-TYPE 에 DEPOSIT 을 옮기고 상세 줄을 쓴다. W 도 같은 모양이다. 그 밖의 글자는 WHEN OTHER 로 가서 거절 건수만 올리고 상세 줄은 쓰지 않는다. 두 갈래가 같은 상세 줄 문단 135-WRITE-DETAIL 을 쓰므로 줄을 만드는 코드가 한 곳에 모여 있다.
135-WRITE-DETAIL 에서 MOVE TR-AMT TO DL-AMT 는 숫자 7자리를 편집 모양으로 바꾸는 부분이다. 150000 은 " 150,000" 이 되고, 30000 은 " 30,000" 이 된다. DL-LINE 을 WS-PRINT-LINE 으로 옮기면 40칸이 그대로 복사되고, 800-WRITE-LINE 이 그 줄을 쓴다.
140-WRITE-TOTALS 는 구분선, 입금 줄, 출금 줄, 거절 줄을 차례로 쓴다. 입금과 출금은 같은 TL-LINE 을 라벨과 값만 바꾸어 두 번 쓴다. TL-LINE 은 28칸이므로 WS-PRINT-LINE 으로 옮기면 나머지 12칸이 공백으로 채워진다.
800-WRITE-LINE 은 이 프로그램에서 WRITE 가 있는 유일한 곳이다. WRITE REPORT-REC FROM WS-PRINT-LINE 으로 쓰고, 곧바로 쓴 줄 수를 1 올린다. 쓰는 곳이 한 군데이므로 줄 수를 세는 코드를 빼먹을 일이 없다.
200-VERIFY-REPORT 는 방금 쓴 파일을 OPEN INPUT 으로 다시 열고 끝까지 읽는다. NOT AT END 쪽에서 읽은 줄 수를 올리고 줄 내용을 화면에 표시한다. 다 읽으면 닫고 쓴 줄 수와 읽은 줄 수를 비교해 CHECK: OK 또는 CHECK: MISMATCH 를 표시한다.
실행 결과
$ cobc -x -Wall main.cob
$ ./main
DAILY TRANSACTION REPORT
ACCT TYPE AMOUNT NAME
----------------------------------------
100101 DEPOSIT 150,000 KIM MINSU
100102 WITHDRAWAL 30,000 LEE JIWON
100101 WITHDRAWAL 20,000 KIM MINSU
100203 DEPOSIT 500,000 PARK HANA
100102 DEPOSIT 75,000 LEE JIWON
100304 WITHDRAWAL 120,000 CHOI DOYUN
----------------------------------------
DEPOSIT 3 ITEMS 725,000
WITHDRAWAL 3 ITEMS 170,000
REJECTED 1 ITEMS
LINES WRITTEN: 13
LINES READ BACK: 13
CHECK: OK
프로그램이 끝난 뒤 같은 폴더에 report.txt 가 남는다. 내용은 화면에 표시된 앞의 13줄과 같다.
$ cat report.txt
DAILY TRANSACTION REPORT
ACCT TYPE AMOUNT NAME
----------------------------------------
100101 DEPOSIT 150,000 KIM MINSU
100102 WITHDRAWAL 30,000 LEE JIWON
100101 WITHDRAWAL 20,000 KIM MINSU
100203 DEPOSIT 500,000 PARK HANA
100102 DEPOSIT 75,000 LEE JIWON
100304 WITHDRAWAL 120,000 CHOI DOYUN
----------------------------------------
DEPOSIT 3 ITEMS 725,000
WITHDRAWAL 3 ITEMS 170,000
REJECTED 1 ITEMS
입금은 150,000 + 500,000 + 75,000 = 725,000 이고, 출금은 30,000 + 20,000 + 120,000 = 170,000 이다. 일곱 번째 레코드의 거래 종류는 X 라서 상세 줄에 나오지 않고 거절 1건으로만 센다. 이렇게 입력 레코드 7개에서 상세 줄 6개, 합계 쪽 줄 4개가 나오는 관계를 손으로 맞추어 보면 프로그램을 믿을 근거가 된다.
실무에서 자주 틀리는 것
WRITE 에 파일 이름을 쓴다
READ 의 습관대로 WRITE 에도 파일 이름을 쓰면 컴파일 단계에서 막힌다. 쓰기는 항상 레코드 이름을 대상으로 한다.
*> 틀린 코드
WRITE REPORT-FILE FROM WS-PRINT-LINE
*> 고친 코드
WRITE REPORT-REC FROM WS-PRINT-LINE
출력 레코드에 직접 채워 앞 줄 찌꺼기가 남는다
출력 레코드를 칸으로 나누어 놓고 필요한 칸만 채워 쓰면, 채우지 않은 칸에는 앞 줄의 값이 그대로 남는다.
*> 틀린 코드
FD REPORT-FILE.
01 REPORT-REC.
05 RP-ACCT PIC X(6).
05 RP-NAME PIC X(10).
...
MOVE TR-ACCT TO RP-ACCT
MOVE TR-NAME TO RP-NAME
WRITE REPORT-REC
MOVE "TOTAL" TO RP-ACCT
WRITE REPORT-REC *> RP-NAME 에 앞 이름이 남아 있다
*> 고친 코드: 작업 영역에서 줄을 만들어 FROM 으로 쓴다
MOVE SPACES TO WS-PRINT-LINE
MOVE "TOTAL" TO WS-PRINT-LINE
WRITE REPORT-REC FROM WS-PRINT-LINE
MOVE 로 짧은 글자를 긴 칸에 옮기면 남는 칸이 공백으로 채워지므로, 줄 전체를 한 번에 옮기는 방식이 찌꺼기를 막는다.
닫지 않고 바로 다시 읽는다
쓰기를 마친 파일을 CLOSE 하지 않고 확인하려고 열면, 같은 파일을 두 번 열었다는 오류가 나거나 마지막 줄이 파일에 반영되기 전에 읽게 된다.
*> 틀린 코드
OPEN OUTPUT REPORT-FILE
...
OPEN INPUT REPORT-FILE
*> 고친 코드
OPEN OUTPUT REPORT-FILE
...
CLOSE REPORT-FILE
OPEN INPUT REPORT-FILE
누적 변수가 작거나 시작값이 없다
합계 칸의 자릿수가 모자라면 큰 값의 높은 자리가 잘려 나간다. 시작값이 없으면 이전에 있던 값에서부터 쌓인다.
*> 틀린 코드: 725000 을 5자리에 담으면 25000 이 된다
05 WS-DEP-SUM PIC 9(5).
*> 고친 코드: 자릿수를 넉넉히, 시작값을 명시
05 WS-DEP-SUM PIC 9(7) VALUE 0.
한눈에 보기
| 문장 | 역할 | 주의 | 이 장의 위치 |
|---|---|---|---|
| SELECT ... ASSIGN TO | 파일 이름 연결 | 디스크 이름은 큰따옴표로 쓴다 | FILE-CONTROL |
| OPEN OUTPUT | 쓰기 위해 열기 | 기존 내용을 비운다 | 100-WRITE-REPORT |
| WRITE 레코드 FROM 영역 | 작업 영역을 출력 레코드로 옮겨 쓰기 | 파일 이름이 아니라 레코드 이름 | 800-WRITE-LINE |
| ADD ... TO 누적변수 | 건수·합계 쌓기 | 시작값 0, 자릿수 넉넉히 | 130-PROCESS-TRANS |
| CLOSE 후 OPEN INPUT | 쓴 파일 다시 읽기 | 닫은 뒤에 연다 | 200-VERIFY-REPORT |
| 줄 | 개수 | 작업 영역 이름 | 쓰는 시점 |
|---|---|---|---|
| 머리글 | 3줄 | HD-TITLE, HD-COLUMNS, HD-RULE | 반복 시작 전 |
| 상세 | 거래마다 1줄 | DL-LINE | 반복 안 |
| 합계 | 4줄 | HD-RULE, TL-LINE, RJ-LINE | 반복이 끝난 뒤 |
연습 문제
- 합계 아래에 입금 건수와 출금 건수를 더한 "PROCESSED" 줄을 추가하려 한다. 어떤 변수와 줄 정의가 필요하고, 완성 후 쓴 줄 수는 몇이 되는가.
- OPEN OUTPUT REPORT-FILE 을 OPEN EXTEND REPORT-FILE 로 바꾸고 프로그램을 두 번 실행하면 어떻게 되는가. 확인 단계의 결과도 설명하라.
- 금액 칸을 Z,ZZZ,ZZ9 에서 ZZ,ZZZ,ZZ9 로 한 칸 넓히려 한다. 함께 고쳐야 하는 곳을 모두 말하라.
- trans.dat 가 비어 있는(0줄) 경우 보고서는 어떤 모양이 되는가. 줄 수와 합계 줄의 값을 말하라.
정답과 해설
- 처리 건수를 담을 WS-ALL-CNT PIC 9(3) 과 새 줄 정의가 필요하다. 줄 정의는 RJ-LINE 과 같은 모양에서 라벨만 "PROCESSED" 로 바꾸면 된다. 140-WRITE-TOTALS 에서 ADD WS-DEP-CNT WS-WDR-CNT GIVING WS-ALL-CNT 로 값을 만들고 건수를 옮겨 쓴다. 합계 줄이 하나 늘므로 쓴 줄 수는 14가 된다. 줄 수 대조는 이미 800-WRITE-LINE 에서 세고 있으므로 고칠 필요가 없다.
- OPEN EXTEND 는 기존 파일 끝에 이어 붙인다. 두 번째 실행에서 파일에는 13줄이 더해져 26줄이 되지만, WS-LINES-OUT 은 이번 실행에서 쓴 13줄만 센다. 확인 단계는 파일 전체를 읽어 26줄을 세므로 CHECK: MISMATCH 가 나온다. 이렇게 줄 수 대조가 열기 방식을 잘못 고른 실수를 잡아 준다. 단, WS-LINES-OUT 과 WS-LINES-IN 이 PIC 99 이므로 99줄을 넘기면 자릿수가 모자란다는 점은 따로 신경 써야 한다.
- DL-AMT 와 TL-AMT 의 PIC 를 모두 ZZ,ZZZ,ZZ9 로 바꾸어 각각 10칸으로 만든다. 그러면 DL-LINE 이 41칸, TL-LINE 이 29칸이 되어 REPORT-REC 와 WS-PRINT-LINE 을 PIC X(41) 이상으로 키워야 한다. HD-TITLE, HD-RULE 도 같은 길이로 맞춘다. 머리글 HD-COLUMNS 는 AMOUNT 칸을 한 칸 오른쪽으로 밀리게 하므로 "TYPE" 칸을 14에서 15로 늘려야 금액이 끝나는 위치가 같아진다. 또 금액이 8자리가 되므로 TR-AMT, 누적 변수의 자릿수도 함께 검토해야 한다.
- 읽을 레코드가 없으므로 상세 줄은 없고 머리글 3줄, 합계 쪽 구분선 1줄, 합계 3줄을 합해 7줄이 쓰인다. 입금과 출금 줄은 건수 0, 금액 0이므로 "DEPOSIT 0 ITEMS" 뒤에 금액 칸에 0이 오른쪽 끝에서 표시된다. 거절 줄도 0건이다. 확인 단계는 7줄을 읽어 CHECK: OK 를 표시한다. 입력이 없어도 보고서의 틀은 만들어진다는 점이 중요하다. 담당자는 "오늘 거래 없음"이라는 사실을 보고서로 확인할 수 있다.
READER FEEDBACK
질문·의견
내용에 관한 질문이나 더 나은 설명을 위한 의견을 남겨 주세요. 오탈자는 위의 제보 양식이 더 빨리 반영됩니다. 이 댓글은 원래 게시글과 같은 자리에 쌓입니다.
댓글 0
아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.