Devin.KR

파일 쓰기와 보고서 - 머리글·상세·합계

개발자KR 조회 0

이 장에서 배우는 것

앞 장에서는 거래 파일을 한 레코드(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 의 세 가지 방식과 이미 있는 파일에 대한 동작
방식할 수 있는 일이미 있는 파일은쓰임
OPEN INPUTREAD그대로 둔다거래 파일 읽기
OPEN OUTPUTWRITE내용을 비우고 새로 쓴다매일 새 보고서 만들기
OPEN EXTENDWRITE끝에 이어 붙인다로그 파일에 덧붙이기

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 로 글자를 미리 넣어 둘 수 있다. 둘째, 상세 줄과 합계 줄이 한 레코드 영역을 번갈아 쓰면서 앞 줄의 찌꺼기가 남는 일을 막을 수 있다. 셋째, 줄의 모양을 바꿀 때 파일 선언은 건드리지 않아도 된다.

입력 레코드에서 읽은 값을 작업 영역의 보고서 줄에 만들고, 출력 레코드로 WRITE 한 뒤 파일을 다시 읽어 확인한다.

보고서 줄 레이아웃과 누적

세 가지 줄을 칸 단위로 설계한다

보고서는 머리글(header, 제목과 칸 이름), 상세(detail, 거래 한 건당 한 줄), 합계(total, 마지막 요약)로 이루어진다. 줄마다 모양은 다르지만 금액이 놓이는 칸은 같게 맞추어야 읽기 좋다. 이 장의 보고서는 한 줄을 40칸으로 하고 다음과 같이 나눈다.

보고서 한 줄 40칸의 칸 배분
칸 이름위치(몇 번째 칸)크기내용
계좌1~66계좌번호
종류9~1810DEPOSIT 또는 WITHDRAWAL
금액20~289Z,ZZZ,ZZ9 로 편집한 금액
이름31~4010회원 이름

금액 칸은 앞서 출력 편집을 다룰 때 배운 Z,ZZZ,ZZ9 모양을 그대로 쓴다. 이 형식은 앞쪽 0을 공백으로 바꾸고 천 단위 쉼표를 넣어 주며, 숫자 7자리를 9칸에 담는다. 사이의 빈 칸(7~8, 19, 29~30번째)은 FILLER 로 만든다. FILLER 는 이름이 필요 없는 칸을 뜻하며 VALUE SPACES 로 공백을 채워 둔다.

합계 줄은 상세 줄과 모양이 다르지만 금액이 끝나는 위치는 28번째 칸으로 같다. 라벨 10칸, 건수 3칸, " ITEMS" 6칸, 금액 9칸을 이어 붙이면 정확히 28칸이 된다. 상세 줄의 금액이 끝나는 위치와 같으므로 위아래 금액이 나란히 보인다.

머리글, 상세, 합계 줄은 모양이 달라도 금액 칸이 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반복이 끝난 뒤

연습 문제

  1. 합계 아래에 입금 건수와 출금 건수를 더한 "PROCESSED" 줄을 추가하려 한다. 어떤 변수와 줄 정의가 필요하고, 완성 후 쓴 줄 수는 몇이 되는가.
  2. OPEN OUTPUT REPORT-FILE 을 OPEN EXTEND REPORT-FILE 로 바꾸고 프로그램을 두 번 실행하면 어떻게 되는가. 확인 단계의 결과도 설명하라.
  3. 금액 칸을 Z,ZZZ,ZZ9 에서 ZZ,ZZZ,ZZ9 로 한 칸 넓히려 한다. 함께 고쳐야 하는 곳을 모두 말하라.
  4. trans.dat 가 비어 있는(0줄) 경우 보고서는 어떤 모양이 되는가. 줄 수와 합계 줄의 값을 말하라.

정답과 해설

  1. 처리 건수를 담을 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 에서 세고 있으므로 고칠 필요가 없다.
  2. OPEN EXTEND 는 기존 파일 끝에 이어 붙인다. 두 번째 실행에서 파일에는 13줄이 더해져 26줄이 되지만, WS-LINES-OUT 은 이번 실행에서 쓴 13줄만 센다. 확인 단계는 파일 전체를 읽어 26줄을 세므로 CHECK: MISMATCH 가 나온다. 이렇게 줄 수 대조가 열기 방식을 잘못 고른 실수를 잡아 준다. 단, WS-LINES-OUT 과 WS-LINES-IN 이 PIC 99 이므로 99줄을 넘기면 자릿수가 모자란다는 점은 따로 신경 써야 한다.
  3. 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, 누적 변수의 자릿수도 함께 검토해야 한다.
  4. 읽을 레코드가 없으므로 상세 줄은 없고 머리글 3줄, 합계 쪽 구분선 1줄, 합계 3줄을 합해 7줄이 쓰인다. 입금과 출금 줄은 건수 0, 금액 0이므로 "DEPOSIT 0 ITEMS" 뒤에 금액 칸에 0이 오른쪽 끝에서 표시된다. 거절 줄도 0건이다. 확인 단계는 7줄을 읽어 CHECK: OK 를 표시한다. 입력이 없어도 보고서의 틀은 만들어진다는 점이 중요하다. 담당자는 "오늘 거래 없음"이라는 사실을 보고서로 확인할 수 있다.

댓글 0

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

댓글을 남기려면 로그인이 필요합니다.