更新紀錄(2 筆,最後更新 2026年09月04日)
本文的修改紀錄。已保存的更新前版本,可透過附有 DOI 的永久連結閱讀。
引用本文(DOI: 10.5281/zenodo.21616316)
本文保存於 Zenodo。以下同時提供一律指向最新版本的 DOI,以及固定於您正在閱讀版本的 DOI。
Go Komura(2026)。〈讀 COBOL 原始碼之前該先掌握的最小知識〉。小村軟體有限公司。https://doi.org/10.5281/zenodo.21616316 https://comcomponent.com/zh-TW/blog/2026/03/17/001-cobol-minimum-reading-guide/
- DOI(最新版本)
- 10.5281/zenodo.21616316
- DOI(此版本)
- 10.5281/zenodo.22297154
交接、故障處理、廠商套裝軟體的維護。在這類場面裡,某一天突然就有一份 COBOL 原始碼丟到手上。
- 檔案名稱是
.cbl或.cpy - 變數名稱全是大寫
01、05、77、88一路排下來- 出現
PIC S9(7)V99 COMP-3這種介於咒語與會計軟體之間的寫法 - 而且滿篇都是
COPY,只看打開的這個檔案根本看不到全貌
到這裡,多數人都會先卡住,不知道下一步該做什麼。
不過,要讀懂它所需要的地圖沒有那麼大。COBOL 在不同編譯器與產品之間確實有差異,但讀既有業務系統時該先掌握的骨架,跨編譯器相當一致。本文以 IBM 系與典型的業務 COBOL 為前提,梳理給突然要讀原始碼的人的最小組合。
flowchart TB
accTitle: 卡住的原因與地圖的大小
accDescr: 說明全大寫的變數名稱與層級編號、PIC S9(7)V99 COMP-3 這類寫法,以及滿篇 COPY 看不見全貌會讓人卡住,但讀既有業務系統時該先掌握的骨架跨編譯器相當一致,地圖沒有那麼大的圖。
iv1["看不懂的寫法讓人卡住"] --> iv2["但骨架跨編譯器相當一致"]
iv2 --> iv3["先拿到最小組合的地圖"]
iv1 -.-> iv4["滿篇 COPY 看不見全貌"]
圖 1: 看起來像咒語的寫法,其實可以收攏成幾個共通概念。
1. 先講結論(一句話)
先用比較粗略、但實務上派得上用場的說法來講,大致是這樣。
- 與其說 COBOL 是邏輯的語言,不如說它在很大程度上是記錄定義的語言
- 只讀
PROCEDURE DIVISION只能懂一半。要先看DATA DIVISION PIC是項目的形狀,USAGE是用什麼表示法保存COMP-3是 packed decimal。在金額與件數的世界裡很常見88與其說是另一個變數,不如說是給前一個項目的值取的條件名稱REDEFINES是用另一種形狀看同一塊記憶體的機制。不是複製- 只要有
COPY,現在打開的原始碼就還不是完成形。不看 copybook 就看不到全貌 - 只要追得動
PERFORM、IF、EVALUATE、READ、WRITE、CALL,大致的流程就掌握得住 - 舊的原始碼是欄的位置本身就有意義的固定格式。看到的空白不是單純的裝飾1
總之就是 DIVISION、PIC、USAGE、COMP-3、REDEFINES、OCCURS、88、COPY、PERFORM。這幾個讀得懂,迷路的機率就會下降很多。
圖中實線表示始終成立的關係,虛線表示附帶條件的關係(成立條件寫在詳細頁面中各關係的說明中)。關係的完整清單(共 21 條,附依據與可信度)以及主要概念的定義,彙整在知識地圖詳細頁面(日文)。資料:JSON-LD / Turtle
2. 先把 COBOL 當成「資料形狀」的語言
用 C# 或 Java 的感覺去讀,一開始會想先追 if、for 和函式呼叫。
但讀 COBOL 時,在走到那一步之前,先弄清楚「這支程式接收什麼樣的記錄、產生什麼樣的記錄、又持有哪些緩衝區」會更快。
典型的業務 COBOL 大致是下面的流程。
- 從檔案或 DB 讀出記錄
- 放進
WORKING-STORAGE上的項目 - 做條件分支
- 改填到另一筆記錄
- 寫出去
也就是說,比起演算法,先站上檯面的往往是記錄的排列方式。
flowchart TB
accTitle: 典型業務 COBOL 的流程
accDescr: 說明從檔案或 DB 讀出記錄、放進 WORKING-STORAGE 上的項目、做條件分支、改填到另一筆記錄再寫出去,這個典型業務 COBOL 流程的圖。
f1["讀出記錄"] --> f2["放進 WORKING-STORAGE"]
f2 --> f3["做條件分支"]
f3 --> f4["改填到另一筆記錄"]
f4 --> f5["寫出去"]
圖 2: 主角是記錄的流動,演算法夾在中間。
舉例來說,骨架長這樣。
IDENTIFICATION DIVISION.
PROGRAM-ID. SAMPLE01.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT SALES-FILE ASSIGN TO ...
DATA DIVISION.
FILE SECTION.
FD SALES-FILE.
01 SALES-REC.
05 SALE-ID PIC 9(8).
05 SALE-AMOUNT PIC S9(7)V99 COMP-3.
WORKING-STORAGE SECTION.
01 WS-EOF PIC X VALUE 'N'.
88 EOF VALUE 'Y'.
PROCEDURE DIVISION.
PERFORM UNTIL EOF
READ SALES-FILE
AT END
SET EOF TO TRUE
NOT AT END
PERFORM PROCESS-SALE
END-READ
END-PERFORM
STOP RUN.
讀這段程式碼時,該先看的不是 PERFORM,而是 SALE-AMOUNT 的型別和 EOF 的意義。
COBOL 照這個順序讀,整份程式碼會突然安靜下來。
3. 先看四個 DIVISION
COBOL 原始碼首先大致分成 4 個 DIVISION。
| DIVISION | 先看什麼 |
|---|---|
IDENTIFICATION DIVISION |
程式名稱、舊的註解、來歷 |
ENVIRONMENT DIVISION |
檔案、外部資源、輸入輸出的前提 |
DATA DIVISION |
記錄定義、工作區、引數 |
PROCEDURE DIVISION |
實際的處理步驟 |
其中特別重要的是這幾個。
FILE SECTION這裡有輸入輸出檔案的記錄定義WORKING-STORAGE SECTION這裡有平常使用的變數、旗標、計數器、工作緩衝區LOCAL-STORAGE SECTION可能有每次呼叫都重新初始化的區域LINKAGE SECTION可能有從外部傳進來的引數,或子程式的接收口
看到 LINKAGE SECTION 和 PROCEDURE DIVISION USING ... 時,這支程式很可能不是單獨完結,而是接收外部資料才動起來。
flowchart TB
accTitle: LINKAGE SECTION 代表什麼
accDescr: 說明看到 LINKAGE SECTION 和 PROCEDURE DIVISION USING 時,這支程式很可能不是單獨完結,而是接收外部資料才動起來的圖。
lk1["有 LINKAGE SECTION"] --> lk3["可能是接收外部資料才動起來"]
lk2["有 PROCEDURE DIVISION USING"] --> lk3
lk3 -.-> lk4["不是單獨完結的程式"]
圖 3: 看到接收口的定義,就要假設有呼叫端存在再往下讀。
4. 不要被固定格式的外觀嚇到
在舊的 COBOL 裡,原始碼一行之中欄的位置本身就有意義。不知道這件事就去看,永遠也搞不懂「為什麼左邊有一塊奇怪的空白」。1
固定格式大致是這樣分的。
- 第 1 - 6 欄:序號
- 第 7 欄:indicator
- 第 8 - 11 欄:Area A
- 第 12 - 72 欄:Area B
第 7 欄特別重要。
*或/:註解行-:接續行D:debugging line*>:也可以寫在行中途的註解
把欄的位置和內容的對照關係加上刻度來看,就是下面這樣。第 1 行是十位數的刻度,第 2 行是個位數的刻度。
1 2 3 4 5 6 7 8
12345678901234567890123456789012345678901234567890123456789012345678901234567890
SSSSSSIAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB........
S= 序號(第 1 - 6 欄)I= indicator(第 7 欄)A= Area A(第 8 - 11 欄)B= Area B(第 12 - 72 欄).= 第 73 欄以後。有些編譯器會拿來當識別欄,但不影響程式的意義
套到實際的原始碼上,就是這樣。
000100* 這一行第 7 欄是 * 所以是註解
000200 IDENTIFICATION DIVISION.
000300 PROGRAM-ID. SAMPLE01.
000400 DATA DIVISION.
000500 WORKING-STORAGE SECTION.
000600 01 WS-ORDER.
000700 05 WS-ORDER-ID PIC 9(8).
000800 05 WS-LONG-NAME PIC X(30) VALUE 'ABCDEFGHIJKLMNOPQRST
000900- 'UVWXYZ0123'.
讀法的重點有 4 個。
- 第 1 - 6 欄是序號。上面例子裡標成
000100那樣的就是它,和程式的動作無關。也有整片留空白的情況。 - 第 7 欄是 indicator。
*是註解,-是接續行。上面例子的000900行就是接續行,接的是前一行字串常值的後半段。 - 第 8 - 11 欄是 Area A。
DIVISION、SECTION、段落名稱、FD,以及01和77這些層級編號,都要從這裡開始寫。上面例子的IDENTIFICATION DIVISION.和01 WS-ORDER.就是從 Area A 開始。 - 第 12 - 72 欄是 Area B。一般的陳述式,以及
05這類下層層級寫在這裡。上面例子的05 WS-ORDER-ID就是從 Area B 開始。
這裡的空白不是現代意義上的「排版」,有一部分就是語法本身。 在編輯器裡做 tab 轉換、把整段往左靠、或是隨手複製貼上,都會直接把它弄壞。 看舊的原始碼時,請先確認這個檔案是 fixed format 還是 free format。對 fixed 格式的原始碼套用現代的自動排版,Area A 和 Area B 的分界會亂掉,編譯就過不了。
flowchart TB
accTitle: 動固定格式的原始碼之前
accDescr: 說明看舊的原始碼時要先確認是 fixed format 還是 free format,對 fixed 格式的原始碼套用現代的自動排版或 tab 轉換,會讓 Area A 和 Area B 的分界亂掉而編譯不過的圖。
fx1["打開了舊的原始碼"] --> fx2{"fixed 還是 free"}
fx2 -->|"fixed 格式"| fx3["欄的位置本身就是語法"]
fx3 -.-> fx4["自動排版或 tab 轉換會弄壞"]
fx2 -->|"free 格式"| fx5["欄的限制比較寬鬆"]
圖 4: 在排版之前,先把這個檔案的格式確定下來。
5. DATA DIVISION 的最低限度
5.1 層級編號
COBOL 的資料定義不是靠縮排,而是靠層級編號構成階層。2
01 WS-ORDER.
05 WS-ORDER-ID PIC 9(8).
05 WS-AMOUNT PIC S9(7)V99 COMP-3.
05 WS-STATUS PIC X.
88 WS-OK VALUE '0'.
88 WS-ERROR VALUE '9'.
77 WS-COUNT PIC 9(4).
最少記住下面這些就夠用了。
01:一整包的最上層記錄或群組02-49:它下面的階層77:獨立的單一項目88:condition-name。給前一個項目的值取名字366:RENAMES用。遇到的機率不高,但確實存在
重要的是,不要把 88 當成另一個 bool 變數。
並不是另外有一塊叫 WS-OK 的區域,而是當 WS-STATUS 是 '0' 時,可以用 WS-OK 這個名字來讀它。
flowchart TB
accTitle: 88 層級的真面目
accDescr: 說明 88 層級不是獨立的 bool 變數,而是給前一個項目的值取名字的條件名稱,只是在 WS-STATUS 為特定值時可以用 WS-OK 這個名字讀它的圖。
cn1["基底項目 WS-STATUS"] --> cn2{"值是什麼"}
cn2 -->|"是 0 時"| cn3["以 WS-OK 這個名字成立"]
cn2 -->|"是 9 時"| cn4["以 WS-ERROR 這個名字成立"]
cn3 -.-> cn5["並不存在另一塊區域"]
圖 5: 88 不是變數,而是給值取的好讀別名。
另一個重點是,決定階層的不是空白,而是層級編號。
看到的縮排可以當參考,但最後該相信的是 01 / 05 / 10 / 88。2
5.2 PICTURE
PIC 表示那個項目的形狀。
最常看到的是下面這些。
| 寫法 | 大致的意思 |
|---|---|
X |
文字 |
9 |
數字 |
S |
帶正負號 |
V |
小數點只存在於邏輯上 |
X(10) |
10 個字元 |
9(5) |
5 位數的數值 |
S9(7)V99 |
帶正負號,整數 7 位 + 小數 2 位 |
舉例來說,
PIC X(10)→ 10 個字元PIC 9(5)V99→ 5 位整數 + 2 位小數PIC S9(7)V99→ 帶正負號的 7 位整數 + 2 位小數
就是這樣。
這裡特別重要的是 V。
V 並不會真的帶一個 . 字元。
PIC 9(5)V99 會被當成「有 2 位小數的數值」處理,但資料裡並沒有點字元。
所以把檔案或傾印當成「看得到的字串」來解讀,大多會跌一跤。
flowchart TB
accTitle: V 是邏輯上的小數點
accDescr: 說明 PIC 9(5)V99 的 V 是邏輯上的小數點,並不會真的帶一個點字元,資料裡也沒有點,所以把檔案或傾印當成看得到的字串來解讀就會跌一跤的圖。
vd1["PIC 9(5)V99"] --> vd2["當成有 2 位小數的數值處理"]
vd2 --> vd3["資料裡不會放進點字元"]
vd3 -.-> vd4["當成看得到的字串就會跌一跤"]
圖 6: 小數點只在定義裡,不在資料裡。
5.3 USAGE / DISPLAY / COMP / COMP-3
如果說 PIC 是形狀,USAGE 就是用什麼表示法保存。
最少掌握下面這些,就能讀懂相當多了。45
| 寫法 | 大致的意思 | 讀的時候要注意 |
|---|---|---|
DISPLAY |
以字元形式呈現的外部十進位 | 在 mainframe 上有時是以 EBCDIC 為前提6 |
COMP / BINARY |
二進位 | 看到的位數和內部表示法是兩回事 |
COMP-3 / PACKED-DECIMAL |
packed decimal | 當成字元讀會看起來壞掉 |
舉例來說,
01 WS-AMOUNT-DISP PIC S9(7)V99.
01 WS-AMOUNT-BIN PIC S9(7) COMP.
01 WS-AMOUNT-PACK PIC S9(7)V99 COMP-3.
這 3 個全都是「數值」,但內容的保存方式不同。
flowchart TB
accTitle: 同樣的數值,保存方式卻不同
accDescr: 說明同樣形狀的帶正負號數值項目,DISPLAY 是以字元形式呈現的外部十進位、COMP 是二進位、COMP-3 是 packed decimal,會因為 USAGE 不同而讓內容的保存方式不同的圖。
us1["同樣形狀的數值項目"] --> us2["DISPLAY〔以字元形式呈現〕"]
us1 --> us3["COMP〔二進位〕"]
us1 --> us4["COMP-3〔packed decimal〕"]
us4 -.-> us5["當成字元讀會看起來壞掉"]
圖 7: 就算 PIC 一樣,USAGE 一變,位元組就是另一回事。
實務上最派得上用場的,是看到 COMP-3 那一瞬間的反應。
- 它就是 packed decimal
- 大概是金額、稅額、件數、比率這一類
- 當成文字看會壞掉是理所當然的
- 用 CSV 或 UTF-8 的心情去看就會出事
先有這層理解,看到傾印或二進位檔案的樣子時,就不會白白慌張。
flowchart TB
accTitle: 看到 COMP-3 當下的反應
accDescr: 說明看到 COMP-3 就知道它是 packed decimal,多半是金額、稅額、件數或比率這類項目,當成文字看會壞掉是理所當然的,因此不要用 CSV 或 UTF-8 的心情去看的圖。
cp1["找到了 COMP-3"] --> cp2["知道它是 packed decimal"]
cp2 --> cp3["懷疑是金額或件數的項目"]
cp3 --> cp4["就算文字看起來壞掉也不慌張"]
圖 8: 光是養成這個反射,在傾印面前慌張的次數就會減少。
COMP-3 實際上會變成什麼樣的位元組
這一段只要親手推一次,之後看東西的方式就會不一樣。
- 一個位元組塞進 2 位十進位數字
- 但最右邊那個位元組例外,用最低 1 位和正負號佔掉 1 個位元組
正負號用 4 位元的值表示,C 是正,D 是負,F 是不帶正負號。
用 IBM 手冊上刊載的例子來核對。4
| 定義 | 值 | 位元組 |
|---|---|---|
PIC S9(4) PACKED-DECIMAL |
+1234 |
01 23 4C |
PIC S9(4) PACKED-DECIMAL |
-1234 |
01 23 4D |
PIC 9(4) PACKED-DECIMAL |
1234 |
01 23 4F |
1234 是 4 位數,因為規則 2 的關係,開頭會多出 1 位的空位。那就是最前面的 0。
用同樣的方式,來推一次本文出現過很多次的 PIC S9(7)V99 COMP-3。
S9(7)V99是整數 7 位 + 小數 2 位 = 9 位V只是表示小數點的位置,一個位元組也不會消耗- 9 位每 2 位塞一個位元組是 4 個位元組,剩下的 1 位和正負號再用 1 個位元組。合計 5 個位元組
值如果是 +12345.67,補齊成 9 位就是 001234567,所以會變成這樣。
值 : +12345.67
9 位表示 : 0 0 1 2 3 4 5 6 7 與正負號
位元組 : 00 12 34 56 7C
^ 正負號 C = 正
如果是負值 -12345.67,只是最後變成 7D 而已。
位元組 : 00 12 34 56 7D
重點是把這串當成文字打開時會看到什麼。硬把 00 12 34 56 7C 一個位元組對一個字元地對到 ASCII 上,
00是 NUL,12是控制字元,本來就沒辦法當成字元顯示34是4,56是V,7C是|
就會變成這樣。也就是說,畫面上看起來像是「幾個無法顯示的字元後面接著 4V|」。12345.67 這個排列在任何地方都不會出現。
這就是「看起來像亂碼,其實沒有壞掉」的真面目。打開傾印遇到意義不明的排列時,請先懷疑那個項目的 USAGE 是不是 COMP-3。
flowchart TB
accTitle: COMP-3 的位元組是怎麼組出來的
accDescr: 說明 packed decimal 把十進位數字每 2 位塞進 1 個位元組,只有最右邊的位元組用最低 1 位和正負號,所以 9 位的數值是 5 個位元組,而把它當成 ASCII 打開時原本的數值排列不會出現在任何地方的圖。
pk1["十進位數字每 2 位塞進 1 個位元組"] --> pk2["最後一個位元組放最低 1 位和正負號"]
pk2 --> pk3["9 位就是合計 5 個位元組"]
pk3 --> pk4["用 ASCII 打開看不到原本的數值"]
pk4 -.-> pk5["看起來像亂碼,其實沒有壞掉"]
圖 9: 知道這兩條塞法規則,意義不明的傾印就變成讀得懂的一排位元組。
順便附上從位數換算位元組數的速查表。位元組數用「9 的個數除以 2 無條件捨去,再加 1」就算得出來。
| PICTURE 裡 9 的個數 | COMP-3 的位元組數 |
|---|---|
| 1 | 1 |
| 2 - 3 | 2 |
| 4 - 5 | 3 |
| 6 - 7 | 4 |
| 8 - 9 | 5 |
| 10 - 11 | 6 |
| 12 - 13 | 7 |
同一個位元組數會排到 2 種位數,是因為只有 9 的個數是偶數時,開頭才會多出 1 位的空位。S9(4) 會變成 01 23 4C 這 3 個位元組,而 S9(5) 同樣塞得進 3 個位元組,原因就在這裡。
和外部檔案核對排列方式時,少了這張表就會一個位元組一個位元組地錯開下去。
再補充一點,DISPLAY 也不代表一定就是 ASCII 字串。
z/OS 系是以 EBCDIC 為前提,所以就算數字看起來像文字,位元組值也可能和 ASCII 的 '0' - '9' 不同。6
5.4 REDEFINES / OCCURS / COPY / FILLER
這 4 個是閱讀時常見的卡關處。
REDEFINES
REDEFINES 是用另一種形狀看同一塊區域的機制。不是複製。7
01 REC-BUF.
05 REC-TYPE PIC X.
05 REC-DATA PIC X(99).
01 HEADER-REC REDEFINES REC-BUF.
05 HDR-TYPE PIC X.
05 HDR-DATE PIC 9(8).
05 FILLER PIC X(91).
這比較接近 C 系語言裡 union 的感覺。
常見的寫法是「把同一塊 100 個位元組,當成不同的記錄種別來分辨」。
flowchart TB
accTitle: REDEFINES 是同一塊區域的另一種解讀
accDescr: 說明 REDEFINES 不是複製,而是用另一種形狀看同一塊記憶體區域的機制,同一個緩衝區既可以當成一般記錄也可以當成標頭記錄來讀,改寫其中一邊另一邊看起來也會跟著變的圖。
rd1["同一塊記憶體區域"] --> rd2["當成 REC-BUF 來看"]
rd1 --> rd3["當成 HEADER-REC 來看"]
rd2 -.-> rd4["寫了其中一邊,兩邊看到的都會變"]
rd3 -.-> rd4
圖 10: 就算有兩份定義,實體的位元組也只有一份。
OCCURS
OCCURS 就是陣列。在 COBOL 裡習慣叫它 table。
05 WS-ITEM OCCURS 12 TIMES.
10 WS-PRICE PIC 9(5).
再進一步,看到 OCCURS DEPENDING ON 就是可變長度的 table。
這種情況連後續項目的位置都可能受影響,用固定長度的心情去追就會踩空。8
flowchart TB
accTitle: OCCURS DEPENDING ON 的注意事項
accDescr: 說明 OCCURS 是陣列,加上 DEPENDING ON 就變成可變長度的 table,連後續項目的位置都可能隨值移動,因此用固定長度的心情去算位移就會踩空的圖。
oc1["找到了 OCCURS"] --> oc2{"有沒有加 DEPENDING ON"}
oc2 -->|"沒有加"| oc3["固定次數的 table"]
oc2 -->|"有加"| oc4["可變長度的 table"]
oc4 -.-> oc5["後續項目的位置也可能移動"]
圖 11: DEPENDING ON 這三個字一出現,位移計算的前提就變了。
COPY
COPY 是 compile time 的 include。
也就是說,現在打開的這份原始碼可能還不是完成形。9
COPY CUSTOMER-REC.
COPY ERROR-MAP.
記錄定義、共通旗標、SQL 用的 host variable、外部介面被塞進 copybook,是相當常見的事。
COPY 太多讀不下去時,先查看能不能拿到展開後的原始碼或 compiler listing 會比較快。IBM Enterprise COBOL 還有一個叫 MDECK 的 option,可以把函式庫處理後的輸入原始碼寫出來。10
flowchart TB
accTitle: 有 COPY 的原始碼怎麼讀
accDescr: 說明 COPY 是編譯時期的 include,打開的原始碼可能不是完成形,因此要打開 copybook 確認,讀不下去時再去找展開後的原始碼、compiler listing 或 MDECK 選項的輸出的圖。
cy1["找到了 COPY"] --> cy2["打開的原始碼可能還不是完成形"]
cy2 --> cy3["打開 copybook 確認"]
cy2 -.->|"讀不下去時"| cy4["找展開後的原始碼或 listing"]
圖 12: 現在看得到的行數,未必就是這支程式的全部。
FILLER
FILLER 是沒有名字的項目。
但這不代表「因為不會被參考所以沒有意義」。
- 保留區域
- 為了和舊規格相容而留的空洞
- 湊記錄長度
REDEFINES用的空白
這些用途都實際派得上用場。
FILLER 只是沒有名字,位元組數是實際存在的。 忘了這件事,和外部檔案的對應關係就會一個位元組一個位元組地整片錯開。
flowchart TB
accTitle: 漏數 FILLER 會發生什麼事
accDescr: 說明 FILLER 只是沒有名字,位元組數是實際存在的,會作為保留區域或湊記錄長度發揮作用,因此漏數之後和外部檔案的對應關係會一個位元組一個位元組地錯開的圖。
fl1["FILLER 是沒有名字的項目"] --> fl2["位元組數是實際存在的"]
fl2 --> fl3{"有沒有算進排列方式"}
fl3 -->|"算進去了"| fl4["和外部檔案對得上"]
fl3 -->|"忘記算了"| fl5["一個位元組一個位元組地錯開"]
圖 13: 不會被參考的項目,也擔著長度這個職責。
6. PROCEDURE DIVISION 的最低限度
如果說 DATA DIVISION 是地圖,PROCEDURE DIVISION 就是移動路線。
6.1 PERFORM
PERFORM 是 COBOL 基本的控制轉移。
粗略地說,就是呼叫一段處理然後回來。11
常見的形式如下。
PERFORM INIT-PROC
PERFORM UNTIL EOF
PERFORM READ-PROC
IF NOT EOF
PERFORM EDIT-PROC
PERFORM WRITE-PROC
END-IF
END-PERFORM
PERFORM 大致分成 2 個系統。
- 指定段落或節的 out-of-line
PERFORM - 直接在原地寫一段區塊的 inline
PERFORM ... END-PERFORM
更舊的程式碼裡,還常常會出現 PERFORM A-100 THRU A-199 這種範圍指定。
這很方便,但在中間插入段落很容易把不該執行的段落也捲進去,所以讀的時候要好好確認範圍的終點。
flowchart TB
accTitle: PERFORM 的三種形式
accDescr: 說明 PERFORM 有指定段落或節、呼叫後再回來的 out-of-line 形式,也有直接在原地寫一段區塊的 inline 形式,舊程式碼還有用 THRU 做範圍指定的形式,而範圍指定在中間插入段落很容易把不該執行的段落捲進去的圖。
pf1["找到了 PERFORM"] --> pf2["呼叫段落的 out-of-line"]
pf1 --> pf3["寫在原地的 inline"]
pf1 --> pf4["用 THRU 的範圍指定"]
pf4 -.-> pf5["一定要確認範圍的終點"]
圖 14: PERFORM 是哪一種形式,決定了該追的返回點與範圍。
6.2 IF / EVALUATE / 範圍
條件分支基本上用 IF。
EVALUATE 想成類似 switch/case 的東西,大致上不會錯。
要注意的是範圍是怎麼結束的。12
END-IFEND-PERFORMEND-READ
有這類明確終止子的程式碼還算好讀。
問題出在舊的程式碼。在 COBOL 裡 . 會當成隱含的 scope terminator,把還沒有結束的陳述式一次全部收掉。12
也就是說,只憑一個句點,
IF管到哪裡PERFORM管到哪裡- 在哪裡進到下一個 sentence
就會跟著改變。
還有,NEXT SENTENCE 和 CONTINUE 不一樣。
NEXT SENTENCE 會跳到下一個句點的後面,所以後面 . 的位置一變,跳過去的地方也跟著變。12
讀舊的 COBOL 時,抱著不看行尾、只看句點的態度剛剛好。
flowchart TB
accTitle: 句點的份量
accDescr: 說明有 END-IF 這類明確終止子的程式碼比較好讀,但舊程式碼裡句點會當成隱含的 scope terminator,把還沒結束的陳述式一次收掉,因此一個句點就能改變 IF 或 PERFORM 的範圍的圖。
sc1{"有沒有明確的終止子"}
sc1 -->|"有 END-IF 之類"| sc2["範圍好讀"]
sc1 -->|"沒有的舊程式碼"| sc3["句點就是隱含的終止子"]
sc3 --> sc4["一個句點的位置就改變範圍"]
sc4 -.-> sc5["不看行尾,只看句點"]
圖 15: 舊程式碼的控制流程,是由句點的位置掌控的。
6.3 READ / WRITE / CALL
業務 COBOL 裡最常出現的是這幾個。
READWRITEREWRITESTARTCALL
其中 READ ... AT END ... 是最正統的做法。
READ IN-FILE
AT END
SET EOF TO TRUE
NOT AT END
PERFORM PROCESS-REC
END-READ
只要有 CALL 'SUBPGM' USING ...,就會跳到另一支程式。
這時去看被呼叫端的 LINKAGE SECTION 和 PROCEDURE DIVISION USING,參數傳遞的形狀就看得相當清楚。
flowchart TB
accTitle: 找到 CALL 之後怎麼追
accDescr: 說明只要有 CALL 就會跳到另一支程式,因此去看被呼叫端的 LINKAGE SECTION 和 PROCEDURE DIVISION USING,就能看清楚引數傳遞的形狀的圖。
ca1["找到了 CALL"] --> ca2["會跳到另一支程式"]
ca2 --> ca3["看被呼叫端的 LINKAGE SECTION"]
ca3 --> ca4["用 USING 掌握傳遞的形狀"]
圖 16: 呼叫的意義,要和被呼叫端的接收口一起讀。
7. 位在 COBOL 外側的東西
COBOL 常常沒辦法只靠原始碼就把整個世界講完。
- 檔案定義
- 執行環境
- DB 連線
- 交易環境
- job 控制
因為這些都被分到外側去了。
至少先掌握下面這幾項,會好讀很多。
檔案與 FILE STATUS
ENVIRONMENT DIVISION 的 FILE-CONTROL,和 DATA DIVISION 的 FILE SECTION / FD 要成套一起讀。13
SELECT IN-FILE ASSIGN TO ...
FILE STATUS IS WS-FS.
FD IN-FILE.
01 IN-REC.
05 ...
只要有 FILE STATUS,每次 I/O 之後的結果碼就會寫進去。
要讀檔案相關的故障或 EOF 判斷,不看它就無從開始。14
flowchart TB
accTitle: 檔案定義要成套一起讀
accDescr: 說明 ENVIRONMENT DIVISION 中 FILE-CONTROL 的 SELECT 和 DATA DIVISION 的 FD 要成套一起讀,只要有 FILE STATUS,每次輸入輸出之後的結果碼就會寫進去,因此檔案相關的故障和 EOF 判斷都從這裡讀起的圖。
fs1["FILE-CONTROL 的 SELECT"] --> fs3["兩者合起來是一份檔案定義"]
fs2["FILE SECTION 的 FD"] --> fs3
fs3 --> fs4["FILE STATUS 會寫進結果碼"]
fs4 -.-> fs5["解讀故障與 EOF 判斷的起點"]
圖 17: 檔案的樣貌,是分寫在兩個 DIVISION 裡的。
EXEC SQL
出現這個就是內嵌 SQL。
EXEC SQL
SELECT ...
END-EXEC.
這種情況下,COBOL 是「host variable 的容器」,實際的取得條件和更新對象都在 SQL 那一側。
所以把 EXEC SQL 的內容當成一般 SQL 來讀是捷徑。
EXEC CICS
出現這個就是 CICS 的交易脈絡。15
EXEC CICS
RECEIVE MAP(...)
END-EXEC.
到這一刻,就不再只是讀批次程式了。 畫面、交易、回應碼、COMMAREA 等等,都必須連同外部脈絡一起讀。
JCL 與執行定義
在 mainframe batch 裡,實際會配置到哪個資料集、job 以什麼順序流動寫在 COBOL 原始碼外面,一點也不稀奇。 只看原始碼卻搞不清楚「這個檔案在哪裡」時,常常不是程式碼有問題,而是看的範圍還不夠大。
flowchart TB
accTitle: 位在原始碼外面的世界
accDescr: 說明出現 EXEC SQL 時實際的取得條件在 SQL 那一側,出現 EXEC CICS 時畫面與交易等外部脈絡在外面,mainframe batch 的資料集配置和 job 流動則寫在 JCL 裡,整個世界分在 COBOL 原始碼外側的圖。
ex1["EXEC SQL"] --> ex7["連原始碼外面也要看"]
ex3["EXEC CICS"] --> ex7
ex5["JCL 與執行定義"] --> ex7
ex7 -.-> ex2["SQL 的條件、畫面脈絡、job 的流動"]
圖 18: 看不懂未必是程式碼的錯,有時只是看的範圍太窄。
編譯器實作的差異
本文是以 IBM 系為前提寫的,但實務上也會遇到 Micro Focus,或是跑在 Linux / Windows 上的 COBOL。骨架共通,所以讀法不變,但「寫法一樣,結果卻不同」的地方就是固定那幾個,先掌握起來就不會出事。
| 要看的地方 | z/OS 系(IBM Enterprise COBOL) | 開放系統(Micro Focus、Linux / Windows 版等) |
|---|---|---|
| 字元編碼 | 以 EBCDIC 為前提6 | 以 ASCII 為前提6 |
| 參考格式 | fixed 格式是傳統上的預設。也可以選 free 格式1 | fixed 和 free 兩種都有,預設是哪一種要看建置設定1 |
| copybook 的尋找方式 | 指定函式庫 | 用編譯器選項指定搜尋路徑 |
| 方言 | — | 有可以切換「要配合哪一個編譯器」的選項 |
影響最大的是字元編碼。同樣是 PIC X(10),在 z/OS 上寫出來的檔案直接拿到 Windows 這邊讀,連數字都會變成不同的位元組值。「搬過去之後全部變亂碼」多半就是這個原因,和 COMP-3 是兩回事。
還有一個地方也容易出現差異,就是數值的內部表示法。在 IBM Enterprise COBOL 裡,BINARY / COMP-4 會依照 PICTURE 上寫的位數進行截斷,而 COMP-5 則是一路持有到 2 / 4 / 8 個位元組這種原生二進位容量為止,截斷也發生在二進位大小那一側。16 也就是說,PIC S9(4) COMP 和 PIC S9(4) COMP-5 看起來一樣,能放進去的值的上限卻不同。如果在直接以二進位和其他系統交換數值的程式碼裡看到 COMP-5,請當成作者是刻意這樣寫的來讀。
要和自己的環境核對時,最短的步驟如下。
- 先打開建置定義(makefile、JCL、專案設定)。 用的是哪一個編譯器、哪些選項編譯的,會比讀原始碼更早弄清楚。
- 確定參考格式(fixed / free)。 這裡沒弄清楚就在編輯器裡排版,檔案會壞掉。
- 確定字元編碼。 是 EBCDIC 還是 ASCII,傾印的讀法完全不同。
- 在
COMP系列的項目上做記號。 編譯器差異幾乎都出在這裡。
flowchart TB
accTitle: 和自己的環境核對的最短步驟
accDescr: 說明先打開建置定義確認是哪一個編譯器與哪些選項,接著確定參考格式是 fixed 還是 free,再確定字元編碼是 EBCDIC 還是 ASCII,最後在容易出現編譯器差異的 COMP 系列項目上做記號的步驟的圖。
ck1["先打開建置定義"] --> ck2["確定參考格式"]
ck2 --> ck3["確定字元編碼"]
ck3 --> ck4["在 COMP 系列項目上做記號"]
圖 19: 讀原始碼之前的這四步,就能擋掉編譯器差異造成的事故。
8. 最低限度的閱讀順序
突然要讀 COBOL 時,照下面的順序比較安全。
- 把
COPY全部盤點一遍 copybook 打得開就打開。打不開就找 listing 或展開後的原始碼 - 撿出
01層級的記錄定義 把FILE SECTION、WORKING-STORAGE、LINKAGE SECTION的最上層整理成清單 - 讀
PIC和USAGE辨識出金額、日期、件數、代碼、旗標 - 搜尋
READ/WRITE/REWRITE/CALL/EXEC SQL/EXEC CICS先掌握輸入輸出和外部邊界 - 只追第一條主路徑
從
PROCEDURE DIVISION的開頭沿著PERFORM鏈走一遍 - 看
88和 status 項目 EOF、正常與異常、種別碼的意義會變得好讀 - 在
REDEFINES/OCCURS DEPENDING ON/COMP-3上做記號 之後一定用得上,先當成危險物標起來 - 是檔案的話就看
FILE STATUS可以大幅減少 I/O 錯誤這一類的誤讀
照這個順序,就不必一開始就把全文精讀一遍。 讀 COBOL 與其一開始就想 100% 理解,不如先掌握記錄、外部邊界、主路徑這 3 點再往細節走,會輕鬆得多。
flowchart TB
accTitle: 安全的閱讀順序
accDescr: 說明先把 COPY 盤點一遍並確認 copybook,把 01 層級的記錄定義整理成清單,用 PIC 和 USAGE 讀出項目的形狀,用搜尋掌握輸入輸出與外部邊界,再從 PROCEDURE DIVISION 開頭沿著 PERFORM 鏈追主路徑,最後在危險物上做記號的閱讀順序的圖。
ro1["把 COPY 全部盤點一遍"] --> ro2["把 01 層級整理成清單"]
ro2 --> ro3["用 PIC 和 USAGE 讀出形狀"]
ro3 --> ro4["搜尋輸入輸出與外部邊界"]
ro4 --> ro5["追主路徑的 PERFORM 鏈"]
ro5 -.-> ro6["先在危險物上做記號"]
圖 20: 不是全文精讀,而是照這個順序由外往內收攏。
8.1 練習:讀讀看這段記錄定義
光講讀法不容易記住,所以這裡放一段小型的記錄。請先自己算出答案,再看下面的解答。
01 CUST-REC.
05 CUST-ID PIC X(8).
05 CUST-NAME PIC X(20).
05 CUST-KBN PIC X.
88 CUST-NORMAL VALUE '0'.
88 CUST-VIP VALUE '1'.
05 CUST-BALANCE PIC S9(7)V99 COMP-3.
05 CUST-HIST OCCURS 3 TIMES.
10 HIST-DATE PIC 9(8).
10 HIST-AMOUNT PIC S9(5)V99 COMP-3.
05 FILLER PIC X(4).
問題
CUST-REC整體是幾個位元組?CUST-BALANCE從記錄開頭數起,是從第幾個位元組開始?- 第 2 筆的
HIST-AMOUNT從開頭數起,是從第幾個位元組開始? CUST-KBN裡放的是'1'時,會成立的條件名稱是哪一個?- 用文字編輯器打開這個檔案時,看起來會壞掉的是哪些項目?
- 請舉出 2 件光看這段定義無法知道的事。
解答
-
是 74 個位元組。明細如下。
項目 計算 位元組數 CUST-IDX(8)8 CUST-NAMEX(20)20 CUST-KBNX1 CUST-BALANCE9 位的 COMP-3 5 CUST-HIST(8 + 4)× 3 次36 FILLERX(4)4 合計 74 88層級是條件名稱,所以不會消耗位元組。把它也算進去是很常見的錯誤。FILLER只是沒有名字,那 4 個位元組確確實實存在。 -
從第 30 個位元組開始。前面有
8 + 20 + 1 = 29個位元組,所以從下一個開始。 -
從第 55 個位元組開始。
CUST-HIST是從第 35 個位元組開始,1 筆是 12 個位元組,所以第 1 筆是第 35 - 46 個位元組,第 2 筆是第 47 - 58 個位元組。其中開頭 8 個位元組是HIST-DATE,所以HIST-AMOUNT是從第 55 個位元組開始。 -
是
CUST-VIP。並不是另外存在一塊叫CUST-VIP的區域,而是當CUST-KBN是'1'時可以用那個名字讀它。 -
是
CUST-BALANCE和HIST-AMOUNT。兩個都是COMP-3,當成字元讀就會變成沒有意義的排列。HIST-DATE是PIC 9(8)的DISPLAY,在 ASCII 環境下可以像20260317一樣當成數字讀出來。不過在 EBCDIC 環境下,看起來像數字,位元組值卻和 ASCII 不同。 -
例如下面這些。
- 這段定義本身是不是由
COPY帶進來的。 不看 copybook 那一側,就不知道實際使用的是不是這一版。 - 檔案的字元編碼是 EBCDIC 還是 ASCII。
PIC X和PIC 9 DISPLAY呈現出來的樣子會不一樣。 - 維運上
CUST-KBN會不會出現'0'和'1'以外的值。 條件名稱只定義了 2 個,但這並不保證不會有其他值進來。 - 檔案本身的屬性(記錄長度、是不是可變長度、
FILE STATUS)。 這要看ENVIRONMENT DIVISION和FD才知道。
- 這段定義本身是不是由
第 1 題和第 3 題答錯的話,請回到 5.3 的位數與位元組數對照表。COBOL 的誤讀,多半就是從這裡開始的。
9. 常見的卡關處
最後歸納一下初學者很高機率會卡住的地方。
把 REDEFINES 當成「另一個變數」
不對。 它是用另一種形狀讀同一塊區域。改寫其中一邊,另一邊看起來也會跟著變。7
把 88 當成「獨立的 bool」
不對。
它只是給前一個項目的值取了名字。SET WS-OK TO TRUE 在背後是把對應的值寫進基底項目。3
忽略 COPY 只讀本文
打開的檔案還只是整體的一半。 欄位定義、共通旗標、host variable 整批都在外面,是很平常的事。9
把 MOVE 當成單純的指派
MOVE 不是單純的 memcpy。
會依照接收端的型別,加入轉換、位數對齊、補零、截斷、編輯與反編輯等處理。17
小看 . 的影響
COBOL 的 . 比想像中重得多。
在沒有明確終止子的舊程式碼裡,看錯這個句點收到哪裡為止,就會把控制流程讀錯。12
把 packed decimal 或 EBCDIC 當成「亂碼」
不一定是壞掉了。 很多時候只是它從一開始就不是字串,或者只是不是 ASCII 而已。46
以為 OCCURS DEPENDING ON 後面是固定位置
可變長度 table 後面的項目,位置會隨著值而移動。 用固定長度的思路去讀,位移計算會全部錯開。8
10. 先看這張速查表
| 找到的詞 | 先想到什麼 |
|---|---|
01 |
記錄或群組的最上層。從這裡抓全貌 |
88 |
旗標或狀態碼的意義名稱。讀分支的關鍵 |
PIC X(...) |
文字項目 |
PIC 9(...) / S9(...)V... |
數值項目。確認位數和小數位置 |
COMP |
binary |
COMP-3 |
packed decimal。很可能是金額或件數 |
REDEFINES |
用另一種解讀方式看同一塊區域 |
OCCURS |
陣列、table |
OCCURS DEPENDING ON |
可變長度。後續位置也要注意 |
FILLER |
沒有名字但有長度 |
COPY |
不看 copybook 就看不到完成形 |
PERFORM |
主路徑的骨架 |
READ / WRITE / REWRITE |
檔案 I/O |
EXEC SQL |
DB 處理 |
EXEC CICS |
交易處理 |
FILE STATUS |
I/O 的結果碼 |
11. 總結
COBOL 不是因為老所以難。 是因為資料定義、外部檔案、執行脈絡緊密綁在一起,才讓人一開始看不到入口。
再把閱讀所需的最小組合整理一次,就是這樣。
- 用
DIVISION抓住地圖 - 先讀
DATA DIVISION - 用
PIC和USAGE讀出項目的形狀 - 在
COMP-3、REDEFINES、OCCURS、88、COPY上做記號 - 追
PERFORM、READ、WRITE、CALL - 用
FILE STATUS、EXEC SQL、EXEC CICS掌握外部邊界 - 不要小看
.的作用
看清楚這些之後,COBOL 就會從「謎樣的上古魔法」變成「處理記錄的語言」。 舊技術可怕的地方不在於名字老,而是一開始挑錯了看它的比例尺,就會突然變得難懂。地圖的比例尺對上了,其實讀起來意外地普通。
flowchart TB
accTitle: 對準比例尺再讀
accDescr: 說明先用 DIVISION 抓住地圖,先讀 DATA DIVISION 掌握項目的形狀,再用 PERFORM 和 READ 等追流程,最後用 FILE STATUS、EXEC SQL、EXEC CICS 掌握外部邊界,照這個順序對準比例尺之後,COBOL 就能當成處理記錄的語言正常讀懂的圖。
sm1["用 DIVISION 抓住地圖"] --> sm2["用 DATA DIVISION 讀出形狀"]
sm2 --> sm3["用 PERFORM 和輸入輸出追流程"]
sm3 --> sm4["掌握外部邊界"]
sm4 -.-> sm5["謎樣的上古魔法變成處理記錄的語言"]
圖 21: 難的不是語言太老,而是一開始選的比例尺。
12. 參考資料
這是本文主要的參考來源。內文的上標編號本身就是連到這份清單的連結,從每一項末尾的箭頭可以回到原本的位置。
-
IBM, “Reference format” / IBM, “Area A or Area B” / Micro Focus, “Fixed Format” ↩ ↩2 ↩3 ↩4
-
IBM, “Level-numbers” ↩ ↩2
-
IBM, “Format 2: condition-name value” ↩ ↩2
-
IBM, “Examples: numeric data and internal representation” ↩ ↩2 ↩3 ↩4
-
IBM, “PACKED-DECIMAL (COMP-3)” ↩ ↩2
-
IBM, “The EBCDIC character set” / IBM, “Handling differences in ASCII SBCS and EBCDIC SBCS characters” ↩ ↩2 ↩3 ↩4 ↩5
-
IBM, “REDEFINES clause” ↩ ↩2
-
IBM, “OCCURS DEPENDING ON clause” ↩ ↩2
-
IBM, “COPY statement” ↩ ↩2
-
IBM, “PERFORM statement” / IBM, “Procedure division structure” ↩
-
IBM, “Scope terminators” / IBM, “Coding a choice of actions” ↩ ↩2 ↩3 ↩4
-
IBM, “FILE STATUS clause” / IBM, “Using file status keys” ↩
-
IBM, “Computational items” ↩
-
IBM, “Elementary move rules” ↩
相關文章
共用相同標籤的最新文章。能以相近的主題延伸理解。
接手了沒有原始碼也沒有規格書的系統 ── 不停機維運的實務步驟
本文整理著手維運沒有原始碼、也沒有規格書之業務系統的實務步驟,內容涵蓋現行環境的保全與備份、執行檔・資料庫的盤點、從行為復原規格,以及延命・包裝・重建的判斷。
不能直接使用QR Code的讀取值 ── 錯誤更正通過不代表值就正確
QR Code的錯誤更正,並不是一套「只要更正通過就保證值正確」的機制。本文以樣本圖片與兩種解碼器的實測為依據,說明髒污落點不同就會被讀成另一個值的原因,以及業務系統端必須做的驗證。
PowerShell與REST API串接 ── Invoke-RestMethod的實務
本文整理從PowerShell呼叫公司內部API或SaaS REST API的實務作法。內容涵蓋認證標頭的傳遞方式、日文JSON亂碼的因應對策、4xx/5xx的錯誤處理、429的重試、分頁,以及Proxy與TLS的陷阱。
業務系統的代碼設計 ── 商品代碼・客戶代碼的訂定方式與檢查碼
商品代碼・客戶代碼等業務系統代碼體系的實務指南。有意義代碼與無意義流水號的判斷表、JAN・Luhn等檢查碼算式與C#實作、Excel的0消失對策,乃至位數溢位與遷移一次整理清楚。
故障處理不止於復原 ── 給小型開發團隊的 Postmortem(再發防止)實務範本
把故障處理停留在「修好、道歉就結束」,同樣的故障就會反覆發生。本文把 blameless postmortem 翻譯給小型團隊使用,整理出可在 1 小時內寫完的模板、再發防止策略的強度判斷表,以及實施的分級(triage)。
相關主題
與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。
Windows 技術主題
彙整 KomuraSoft LLC 關於 Windows 開發、故障調查與既有資產活用文章的主題中心。
與本主題相關的服務
本文連結到以下服務頁面,歡迎從最接近的入口查看。
技術諮詢 & 設計審查
從既有 COBOL 資產的解讀方式、改修的入口、外部邊界的掌握,到遷移前的評估梳理,都是與技術諮詢、設計審查相當契合的主題。
故障調查 & 根本原因分析
交接之後隨即發生的故障處理,以及追查 COBOL 資產在哪裡出現不一致的工作,很適合當成缺陷調查與原因分析來推進。
常見問題
整理諮詢這個主題時常見的問題。
- COBOL 的原始碼該從哪裡開始讀?
- 只讀 PROCEDURE DIVISION 只能懂一半。與其說 COBOL 是邏輯的語言,不如說它在很大程度上是記錄定義的語言,所以要先看 DATA DIVISION。安全的閱讀順序是:把 COPY 全部盤點一遍並確認 copybook,把 01 層級的記錄定義整理成清單,用 PIC 和 USAGE 讀出項目的形狀,搜尋 READ、WRITE、CALL、EXEC SQL、EXEC CICS 掌握輸入輸出與外部邊界,最後才從 PROCEDURE DIVISION 開頭沿著 PERFORM 鏈只追主路徑。
- PIC S9(7)V99 COMP-3 是什麼意思?
- PIC 表示項目的形狀,USAGE 表示用什麼表示法保存。S9(7)V99 是帶正負號、整數 7 位加小數 2 位的數值,但 V 只是邏輯上的小數點,資料裡並沒有點字元。COMP-3 就是 packed decimal,常出現在金額、稅額、件數、比率這類項目上。它當成文字看會壞掉是理所當然的,用 CSV 或 UTF-8 的心情去看傾印就會出事。
- COBOL 的 88 層級和 REDEFINES 該怎麼理解?
- 88 不是獨立的 bool 變數,而是給前一個項目的值取名字的條件名稱(condition-name)。SET WS-OK TO TRUE 在背後是把對應的值寫進基底項目。REDEFINES 是用另一種形狀看同一塊記憶體區域的機制,不是複製,感覺上比較接近 C 系語言的 union。因為改寫其中一邊,另一邊看起來也會跟著變,所以常用在把同一塊區域依記錄種別分辨的寫法上。
- COPY 陳述式太多、看不到全貌時該怎麼辦?
- COPY 是編譯時期的 include,所以現在打開的原始碼可能還不是完成形。記錄定義、共通旗標、SQL 用的 host variable、外部介面被塞進 copybook,是相當常見的事。讀不下去時,先查看能不能取得展開後的原始碼或 compiler listing 會比較快;IBM Enterprise COBOL 也有一個叫 MDECK 的選項,可以把函式庫處理後的輸入原始碼寫出來。