【COBOL 書き1-12-9】行数があわない!?GnuCOBOLのCOB_LS_SPLITを正しく捉えられるかが鍵〜仲良く鳥貴族

🧩今日の学び
・件数を数える処理で不正レコードを除外しても、元のファイルからそのレコードが消えるわけではない。
・SORTによって不正レコードの位置が移動すると、値を取り出す順番までずれるため、2回目の読み込みにも同じ有効性判定が必要になる。
・複数の処理で同じデータを扱う場合は、各処理の入口で同じ基準を使い、有効なレコードだけを数えて利用することが重要である。

係長)で、気づいただろ?

なるお)気づくもんかい!

係)さすがにイラッとする時間だぞ。

な)あい…

係)手を使って、中央値出してみろ。

な)って、出てますよ?

係)出せって言ってんだよ。

な)あい…

手計算すると中央値は1650になる

データがこうで…

📝 COBOL naruo2.txt
2025-12-31hoshiimo            02500
2026-01-01gyudon2             00900
2026-01-01curry               01500
2026-01-02gyudon2             00900
2026-01-03hoshiimo            02500
2026-01-04udontenpura         01800

ソートするとこうでしょ?

📝 COBOL naruo2.txt
00900
00900
01500
01800
02500
02500

…てか、干し芋高すぎじゃね?

んで偶数だから、3個目と4つ目の平均だと。

(1500 + 1800)/ 2 = (答え)

…がふッ!

係)どうだ、01200だったか?

な)…。

そうなる可能性も捨てきれないというか。この世の物理法則を考えると、極小な確率で01200になるとかならないとか。

係)ならないだろ!

な)あい…

係)最初に気づけ!

なんで、そうなったかわかるか?

昨日も言ったとおり、改行がうまくいってないから7行目が出現してしまったが故に、計算が狂ったんだよ。

な)ふむむむ…

COUNTは6なのに中央値がまだ合わない

な)え、でも、カウントを6に直しましたよね?それに中央値1200ですよ?

係)ん?

な)1200だから、 900と1500の平均じゃないす?

ってことは、2つ目と3つ目の金額の平均になってないです?

ってことは、7行目じゃなくって、1行目になにか出たんじゃないですか?

だって、3つ目と4つ目の金額を拾ってくるってコードなのに、2つ目と3つ目の金額を拾ってきてるわけで、そうなると、1行目になにか出たってことじゃないんです?

係)あ…、う、うーん…

な)あは!係長もわからないっすか!

係)喜ぶな!くそ!

うーん…どういうことだこれ…

な)オラ、ワクワクすっぞ!

係)わくわくすんな!

ちょっとアイスでも食って待ってろ。

な)おー初めての優しさ!

係)なんでだよ!

いいから話しかけるな。

な)ほーい

係)(ブツブツ)

・・・

はぁ…。

な)わからなかったすか?

係)なんでわからない方になるんだよ!

といっても、うーん、お前に細かく説明してもな…

な)信頼されてる!?

係)今のどこに信頼の要素があるんだよ…

原因はGnuCOBOLのCOB_LS_SPLITだった

簡単に言うと、GnuCOBOLのバージョン違いによる結果の違いだな。

な)ほ?

係)お前がアイスを2つ食ってる間に、色々やってみたんだが…

な)見てた!?

係)GnuCOBOLの現バージョンの仕様、COB_LS_SPLITというランタイム…機能によるものだな。

これは

「レコード長より長い行を読んだらどうするか」を決めるもので、

・機能をオンだと、余分なところは次の行に分割

・機能をオフだと、余分なところは切り捨てる

という違いになる。

な)別に問題ないんじゃ?

係)そうだ。本来ならな。

ただ、今回のGnuCOBOL 3.2環境で確認したところ、ファイル終端の状態が重なって、末尾に余計な1バイトが現れたようなんだ。

(→レコードの最後に01800に0が追加されて、018000となった)

その結果、1レコードとして定義した長さを、1バイトだけ超えてしまった

(→その018000の最後0のが行送りされた)

COB_LS_SPLITは、レコード長を超えた部分を次のレコードへ分割するか、その場で切り捨てるかを決める設定だからな、設定していないと分割となるので、余分な行をつくりだしてしまったということだろう。

な)えー!それ大問題じゃ!?

係)ま、そういうこともあるだろうな。

な)「ぐぬ〜」には優しい!?

係)メインフレームの厳格さを求められているわけでもないしな。

こここまで無料で使わせてもらってんだ、そのぐらいは見なかったことにしてもいいいいだろ。

な)ほむぅ…ほんと、内弁慶っすね…

係)お前にだけだ!

ったく、このまま進めるぞ。

前回のコードがこれで終わってるな。

📝 COBOL sample.cob
IDENTIFICATION DIVISION.
       PROGRAM-ID. NARUO.

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT INPUT-FILE
               ASSIGN TO "naruo2.txt"
               ORGANIZATION IS LINE SEQUENTIAL.
           SELECT SORT-FILE
               ASSIGN TO "sortwork.tmp".
           SELECT OUTPUT-FILE
               ASSIGN TO "naruo2_sorted.txt"
               ORGANIZATION IS LINE SEQUENTIAL.

       DATA DIVISION.
       FILE SECTION.
       FD INPUT-FILE.
       01 INPUT-RECORD.
           05 INPUT-SALE-DATE PIC X(10).
           05 INPUT-ITEM-NAME PIC X(20).
           05 INPUT-AMOUNT    PIC 9(5).

       SD SORT-FILE.
       01 SORT-RECORD.
           05 SORT-SALE-DATE PIC X(10).
           05 SORT-ITEM-NAME PIC X(20).
           05 SORT-AMOUNT    PIC 9(5).

       FD OUTPUT-FILE.
       01 OUTPUT-RECORD.
           05 OUTPUT-SALE-DATE PIC X(10).
           05 OUTPUT-ITEM-NAME PIC X(20).
           05 OUTPUT-AMOUNT    PIC 9(5).

       WORKING-STORAGE SECTION.
       01 EOF-FLAG      PIC X VALUE "N".
       01 COUNT-LOOPING PIC 9(2) VALUE 0.
       01 READ-COUNT    PIC 9(2) VALUE 0.
       01 LOW-POS       PIC 9(2) VALUE 0.
       01 HIGH-POS      PIC 9(2) VALUE 0.
       01 LOW-VAL       PIC 9(5) VALUE 0.
       01 HIGH-VAL      PIC 9(5) VALUE 0.
       01 MEDIAN-VAL    PIC 9(5)V9 VALUE 0.

       PROCEDURE DIVISION.
           SORT SORT-FILE
               ON ASCENDING KEY SORT-AMOUNT
               USING INPUT-FILE
               GIVING OUTPUT-FILE.

           OPEN INPUT OUTPUT-FILE.
           PERFORM UNTIL EOF-FLAG = "Y"
               READ OUTPUT-FILE
                   AT END
                       MOVE "Y" TO EOF-FLAG
                   NOT AT END
                       IF OUTPUT-AMOUNT IS NUMERIC 
                          AND OUTPUT-AMOUNT > 0
                          ADD 1 TO COUNT-LOOPING
                       END-IF
               END-READ
           END-PERFORM.
           CLOSE OUTPUT-FILE.

           COMPUTE LOW-POS  = COUNT-LOOPING / 2.
           COMPUTE HIGH-POS = LOW-POS + 1.

           OPEN INPUT OUTPUT-FILE.
           MOVE "N" TO EOF-FLAG.
           PERFORM UNTIL EOF-FLAG = "Y"
               READ OUTPUT-FILE
                   AT END
                       MOVE "Y" TO EOF-FLAG
                   NOT AT END
                       ADD 1 TO READ-COUNT
                       IF READ-COUNT = LOW-POS
                           MOVE OUTPUT-AMOUNT TO LOW-VAL
                       END-IF
                       IF READ-COUNT = HIGH-POS
                           MOVE OUTPUT-AMOUNT TO HIGH-VAL
                       END-IF
               END-READ
           END-PERFORM.
           CLOSE OUTPUT-FILE.

           COMPUTE MEDIAN-VAL = (LOW-VAL + HIGH-VAL) / 2.
           DISPLAY "COUNT=" COUNT-LOOPING
                   " LOW-POS=" LOW-POS " HIGH-POS=" HIGH-POS.
           DISPLAY "LOW-VAL=" LOW-VAL " HIGH-VAL=" HIGH-VAL.
           DISPLAY "MEDIAN IS " MEDIAN-VAL.
           STOP RUN.

1回目で除外してもゴミは消えていない

一回目のループでIS NUMERICを使って選別しただろ?

それは2回目のループでも必要だということになる。

なんでだと思う?

な)… ゴミがあるから?

係)そういうことだな。

1回目で弾いたのは数えるのをやめただけだ。ファイルからゴミが消えたわけじゃない。

2回目も同じファイルを読むんだぞ。同じゴミが、同じ場所に、同じようにいる。

だから、2回目のループでも必要となるんだ。

ゴミがなかったとしても、大勢に影響がなく、ゴミがあったら止められるわけだからな。

な)ほへー、係長って気遣いできる人だったんすね。

ちょっと見直しました。

係)どういう意味だよ!

2回目のループにも同じ選別を入れる

係)いいから、今の話をもとに直してみろ!

な)ほーい。

考え方は1回目と変わんないから、単純に追加ってことでいいわよね

📝 COBOL sample.cob
           OPEN INPUT OUTPUT-FILE.
           MOVE "N" TO EOF-FLAG.
           PERFORM UNTIL EOF-FLAG = "Y"
               READ OUTPUT-FILE
                   AT END
                       MOVE "Y" TO EOF-FLAG
                   NOT AT END
                       IF OUTPUT-AMOUNT IS NUMERIC   ←ここ
                           AND OUTPUT-AMOUNT > 0    ←ここ
                           ADD 1 TO READ-COUNT
                           IF READ-COUNT = LOW-POS
                               MOVE OUTPUT-AMOUNT TO LOW-VAL
                           END-IF
                           IF READ-COUNT = HIGH-POS
                               MOVE OUTPUT-AMOUNT TO HIGH-VAL
                           END-IF
                       END-IF
               END-READ
           END-PERFORM.

係)実行してみろ。

な)ほいさー!

📝 COBOL sample.cob
IDENTIFICATION DIVISION.
       PROGRAM-ID. NARUO.

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT INPUT-FILE
               ASSIGN TO "naruo2.txt"
               ORGANIZATION IS LINE SEQUENTIAL.
           SELECT SORT-FILE
               ASSIGN TO "sortwork.tmp".
           SELECT OUTPUT-FILE
               ASSIGN TO "naruo2_sorted.txt"
               ORGANIZATION IS LINE SEQUENTIAL.

       DATA DIVISION.
       FILE SECTION.
       FD INPUT-FILE.
       01 INPUT-RECORD.
           05 INPUT-SALE-DATE PIC X(10).
           05 INPUT-ITEM-NAME PIC X(20).
           05 INPUT-AMOUNT    PIC 9(5).

       SD SORT-FILE.
       01 SORT-RECORD.
           05 SORT-SALE-DATE PIC X(10).
           05 SORT-ITEM-NAME PIC X(20).
           05 SORT-AMOUNT    PIC 9(5).

       FD OUTPUT-FILE.
       01 OUTPUT-RECORD.
           05 OUTPUT-SALE-DATE PIC X(10).
           05 OUTPUT-ITEM-NAME PIC X(20).
           05 OUTPUT-AMOUNT    PIC 9(5).

       WORKING-STORAGE SECTION.
       01 EOF-FLAG      PIC X VALUE "N".
       01 COUNT-LOOPING PIC 9(2) VALUE 0.
       01 READ-COUNT    PIC 9(2) VALUE 0.
       01 LOW-POS       PIC 9(2) VALUE 0.
       01 HIGH-POS      PIC 9(2) VALUE 0.
       01 LOW-VAL       PIC 9(5) VALUE 0.
       01 HIGH-VAL      PIC 9(5) VALUE 0.
       01 MEDIAN-VAL    PIC 9(5)V9 VALUE 0.

       PROCEDURE DIVISION.
           SORT SORT-FILE
               ON ASCENDING KEY SORT-AMOUNT
               USING INPUT-FILE
               GIVING OUTPUT-FILE.

           OPEN INPUT OUTPUT-FILE.
           PERFORM UNTIL EOF-FLAG = "Y"
               READ OUTPUT-FILE
                   AT END
                       MOVE "Y" TO EOF-FLAG
                   NOT AT END
                       IF OUTPUT-AMOUNT IS NUMERIC
                           AND OUTPUT-AMOUNT > 0
                           ADD 1 TO COUNT-LOOPING
                       END-IF
               END-READ
           END-PERFORM.
           CLOSE OUTPUT-FILE.

           COMPUTE LOW-POS  = COUNT-LOOPING / 2.
           COMPUTE HIGH-POS = LOW-POS + 1.

           OPEN INPUT OUTPUT-FILE.
           MOVE "N" TO EOF-FLAG.
           PERFORM UNTIL EOF-FLAG = "Y"
               READ OUTPUT-FILE
                   AT END
                       MOVE "Y" TO EOF-FLAG
                   NOT AT END
                       IF OUTPUT-AMOUNT IS NUMERIC
                           AND OUTPUT-AMOUNT > 0
                           ADD 1 TO READ-COUNT
                           IF READ-COUNT = LOW-POS
                               MOVE OUTPUT-AMOUNT TO LOW-VAL
                           END-IF
                           IF READ-COUNT = HIGH-POS
                               MOVE OUTPUT-AMOUNT TO HIGH-VAL
                           END-IF
                       END-IF
               END-READ
           END-PERFORM.
           CLOSE OUTPUT-FILE.

           COMPUTE MEDIAN-VAL = (LOW-VAL + HIGH-VAL) / 2.
           DISPLAY "COUNT=" COUNT-LOOPING
                   " LOW-POS=" LOW-POS " HIGH-POS=" HIGH-POS.
           DISPLAY "LOW-VAL=" LOW-VAL " HIGH-VAL=" HIGH-VAL.
           DISPLAY "MEDIAN IS " MEDIAN-VAL.
           STOP RUN.
💻 ターミナル出力 実行結果
COUNT=06 LOW-POS=03 HIGH-POS=04
LOW-VAL=01500 HIGH-VAL=01800
MEDIAN IS 01650.0

OH!メルシー!

係)だから、どういうことだよ…

最後に生まれたゴミはソートされて先頭へ移動した

な)でも、なんで1行目に変なのが出現したんすか?

係長は7行目に追加したとか言ってませんでした?

係)ぐっ!?

な)係長の見込み違い?やっちゃいました?メルシーの呪い?

係)いいか、7件目に出たのは合ってる。だからCOUNTが7になったんだよ!

しかしな、並べ替えたら先頭に来るってとこまで見てなかった。生まれたのは最後、座ったのは先頭ってことだ。

お前のデブデータに呆れて見落としただけだ!

な)ほーほー

係)ま、まぁ、先にソートファイルを確認していればわかったとはいえるが…(ボソ)

おむすび

な)え、なにか言いました?

係)なにも言ってない!ほら帰るぞ!片付けろ!

な)やったー、監禁解除!

係)人聞きの悪い…

な)鳥貴族行きます?

係)…ま、いいか。

な)やた!

(当然続く)

係長のワンポイント

不正データは「一度無視すれば消える」ものじゃない。
本当は、同じファイルを読むたびに、同じ条件で選別し直す必要がある。
よくある誤解は、1回目のループで除外したから後の処理も安全だと思うことだが、ゴミは同じ場所に残っている。
しかもSORTすると、その不正レコードが別の位置へ移動し、中央の値までずらすことがある。
だから件数を数える処理と値を取り出す処理の両方に、同じ有効性判定を入れる。
COBOLは一度の判定で安心する言語ではなく、処理の入口ごとにデータを確かめる言語だ。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメントする

CAPTCHA