0. 前言
最近的專案, 有將 ACUCOBOL 資料檔轉入至 DB 的需求.
最簡單的方式, 當然是針對每個 COBOL 資料檔, 撰寫 COBOL 程式, 各自轉為轉為固定長度的文字檔, 例如: 有 10 個資料檔, 就寫 10 支轉檔程式; 再採以下任一方案:
方案一: 撰寫 Java 或 C# 程式, 排程後, 定期轉至 DB.
方案二: 建立 SSIS Package (source: flat file, destination: ole db), 排程後, 定期轉至 DB.
但事與願違, 客戶想說, 是否可以採通用目的轉檔程式就好, 把整筆 COBOL 資料, 寫到 Binary Sequential 檔案; 由 Java 或 C# 讀取, 轉至 DB. 這樣就不需額外定義固定長度的文字檔 (即 COBOL 的 FD 檔), COBOL 的轉檔程式也變得很單純.
這是沒錯啦, COBOL 變單純了, 但 Java 或 C# 就會變複雜了.
然而並不是每一個 Java 或 C# 的程式設計師, 都了解 COBOL.
COBOL 主要有 2 種資料型態:
- X: 字元型態.
- 9: 數值型態.
但因為古早的時候, 主機記憶體及磁碟空間有限, 所以有了 COMP 的壓縮方式, 以節省空間; 不只 COMP, 還有一些 SIGN LEADING, SIGN TRAILING ... 等的修飾詞.
筆者的第 1 份工作, 就是用 COBOL 寫醫療系統, 對 COMP 還有一些印象; 所以, 就上網查了一些資料, 並寫了一支 COBOL 程式, 作了一下驗證.
本文主要就是針對 COBOL 數值 COMP 型態的儲存格式, 進行整理.
1. 細節
1.1 編譯選項:
編譯選項, 會影響數值的編排方式.
細節可以參考 ACUCOBOL 的官方文件 (以下的參考文件1). 以下只是舉一些例子.
{編譯選項1}
COMP-4 (對應到 Java 或 C# 為 int) 會受 -D 選項的影響, 而造成 byte 數的差異. 如下圖:
在預設的狀況, 即不作該編譯選項設定, 則:
PIC 9(1) COMP-4 ~~ PIC 9(4) COMP-4 為 2 byte unsigned integer ( 0 to 65,535 )
PIC 9(5) COMP-4 ~~ PIC 9(9) COMP-4 為 4 byte unsigned integer ( 0 to 4,294,967,295 )
PIC S9(1) COMP-4 ~~ PIC S9(4) COMP-4 為 2 byte integer ( -32,768 to 32,767 )
PIC S9(5) COMP-4 ~~ PIC S9(9) COMP-4 為 4 byte integer ( -2,147,483,648 to 2,147,483,647 )
(原理)
PIC 9(4) COMP-4 的最小值為 0, 最大值為 9999.
PIC S9(4) COMP-4 的最小值為 -9999; 最大值為 9999.
PIC 9(9) COMP-4 的最小值為 0, 最大值為 999,9999,999.
PIC S9(9) COMP-4 的最小值為 -999,999,999, 最大值為 999,999,999.
上述的 2 byte, 4 byte integer 的配置, 正好可以容納其值域.
{編譯選項2}
COMP-2, COMP-3 會受到 -Dc 選項的影響, 而造成內容的差異. 如下圖:
COMP-2 的最後 "一個" byte 代表正負值符號. 例如: -Dca 的正值是用 0x0B, 負值是 0x0D
COMP-3 的最後 "半個" byte 代表正負值符號. 例如: -Dca 的正值是用 0xF, 負值是 0xD. 預設即是 0xF, 0xD
{編譯選項3}
SIGN LEADING, SIGN TRAILING, SIGN LEADING SEPARATE, SIGN TRAILING SEPARATE 先說明如下:
狀況一. PIC 9(n) 不論有無 SEPARATE 的, 都佔 n 個 bytes.
狀況二. PIC S9(n) 沒有 SEPARATE 的, 佔 n 個 bytes. 但它要如何表達數值呢? 就是用第1碼或最後1碼作編碼; 而其編碼原則, 會受編譯選項的影響.
狀況三. PIC S9(n) 有 SEPARATE 的, 佔 n + 1 個 bytes.
其中, 狀況二會受到 -Dc 選項的影響, 而造成內容的差異. 如下圖:
[正值編碼] (沒有加 -Dci 的 compile option; 維持原值不變)
'0' -> '0'
'1' -> '1'
...
'9' -> '9'
[正值編碼] (有加 -Dci 的 compile option; 有作編碼)
'0' -> '{'
'1' -> 'A'
'2' -> 'B'
'3' -> 'C'
...
'9' -> 'I'
[負值編碼] (不論是否有加 -Dci 的 compile option, 都是如此編碼)
'0' -> '}'
'1' -> 'J'
'2' -> 'K'
'3' -> 'L'
...
'9' -> 'R'
上圖的測試程式, 可以參考 [附錄一].
1.2 官網範例:
在沒有 -Dci 的編譯選項下. 如下表格及截圖 (截圖是為了明顯標註差異)
TRAILING `1' `2' `3' TRAILING SEPARATE `1' `2' `3' LEADING `1' `2' `3' LEADING SEPARATE `1' `2' `3' COMP-1 00 7B COMP-2 01 02 03 COMP-3 12 3F COMP-4 00 7B COMP-5(68000) 00 7B COMP-5(8086) 7B 00 COMP-6 01 23
TRAILING `1' `2' `L' // 3 --> L TRAILING SEPARATE `1' `2' `3' `-' LEADING `J' `2' `3' // 1 --> J LEADING SEPARATE `-' `1' `2' `3' COMP-1 FF 85 COMP-2 01 02 03 0D COMP-3 12 3D COMP-4 FF 85 COMP-5(68000) FF 85 COMP-5(8086) 85 FF COMP-6 illegal
TRAILING `1' `2' `3' `4' `5' `6' TRAILING SEPARATE `1' `2' `3' `4' `5' `6' LEADING `1' `2' `3' `4' `5' `6' LEADING SEPARATE `1' `2' `3' `4' `5' `6' COMP-1 illegal COMP-2 01 02 03 04 05 06 COMP-3 01 23 45 6F COMP-4 00 01 E2 40 COMP-5(68000) 00 01 E2 40 COMP-5(8086) 40 E2 01 00 COMP-6 12 34 56
TRAILING `1' `2' `3' `4' `5' `O' // 6 --> O TRAILING SEPARATE `1' `2' `3' `4' `5' `6' `-' LEADING `J' `2' `3' `4' `5' `6' // 1 --> J LEADING SEPARATE `-' `1' `2' `3' `4' `5' `6' COMP-1 illegal COMP-2 01 02 03 04 05 06 0D COMP-3 01 23 45 6D COMP-4 FF FE 1D C0 COMP-5(68000) FF FE 1D C0 COMP-5(8086) C0 1D FE FF COMP-6 illegal
2. 結論
若真的要實作客戶想要的方式, 除了要理解 COBOL 數值資料編碼要處理外, Java 或 C# 還要能夠解讀原有 COBOL FD (要計算各欄位的起始位置), 還有 BIG-5 字碼轉換 (造字) 的處理, 並不是那麼單純.
本文主要在介紹 ACUCOBOL COMP 數值的編碼方式, 有些 ANSI COBOL 並沒有明確規定. (例如: SIGN LEADING / SIGN TRAILING, 參考文件1. 第18點), 各家編譯器可能有不同的處理方式, 要留意.
3. 參考文件
- 01. MicroFocus, USAGE Clause
- 02. Stack Overflow, How to import a fixed width flat file into database using SSIS?
4. [附錄一] 測試程式
IDENTIFICATION DIVISION. PROGRAM-ID. PROGRAM1. * 測試 SIGN TRAILING / LEADING ... ENVIRONMENT DIVISION. CONFIGURATION SECTION. INPUT-OUTPUT SECTION. FILE-CONTROL. SELECT PRINTF ASSIGN TO "SIGNTEST.TXT" ORGANIZATION IS LINE SEQUENTIAL. DATA DIVISION. FILE SECTION . FD PRINTF LABEL RECORD OMITTED. 01 PRINT-REC. 05 DATA-PRINT PIC X(128). WORKING-STORAGE SECTION. 01 WORK-AREA. 05 I PIC 9(6) VALUE 0. 05 ANS PIC X(01) VALUE SPACES. 01 MY-DATA-0. 05 CX901 PIC 9(03) SIGN LEADING VALUE 120. 05 FILLER PIC X(01) VALUE X"09". 05 CX902 PIC 9(03) SIGN LEADING VALUE 121. 05 FILLER PIC X(01) VALUE X"09". 05 CX903 PIC 9(03) SIGN LEADING VALUE 123. 05 FILLER PIC X(01) VALUE X"09". 05 CX904 PIC 9(03) SIGN LEADING VALUE 129. 05 FILLER PIC X(01) VALUE X"09". 05 CX905 PIC 9(03) SIGN LEADING SEPARATE VALUE 120. 05 FILLER PIC X(01) VALUE X"09". 05 CX906 PIC 9(03) SIGN LEADING SEPARATE VALUE 121. 05 FILLER PIC X(01) VALUE X"09". 05 CX907 PIC 9(03) SIGN LEADING SEPARATE VALUE 123. 05 FILLER PIC X(01) VALUE X"09". 05 CX908 PIC 9(03) SIGN LEADING SEPARATE VALUE 129. 05 FILLER PIC X(01) VALUE X"09". 01 MY-DATA-1. 05 CX911 PIC 9(03) SIGN TRAILING VALUE 120. 05 FILLER PIC X(01) VALUE X"09". 05 CX912 PIC 9(03) SIGN TRAILING VALUE 121. 05 FILLER PIC X(01) VALUE X"09". 05 CX913 PIC 9(03) SIGN TRAILING VALUE 123. 05 FILLER PIC X(01) VALUE X"09". 05 CX914 PIC 9(03) SIGN TRAILING VALUE 129. 05 FILLER PIC X(01) VALUE X"09". 05 CX915 PIC 9(03) SIGN TRAILING SEPARATE VALUE 120. 05 FILLER PIC X(01) VALUE X"09". 05 CX916 PIC 9(03) SIGN TRAILING SEPARATE VALUE 121. 05 FILLER PIC X(01) VALUE X"09". 05 CX917 PIC 9(03) SIGN TRAILING SEPARATE VALUE 123. 05 FILLER PIC X(01) VALUE X"09". 05 CX918 PIC 9(03) SIGN TRAILING SEPARATE VALUE 129. 05 FILLER PIC X(01) VALUE X"09". 01 MY-DATA-2. 05 CX921 PIC S9(03) SIGN LEADING VALUE 120. 05 FILLER PIC X(01) VALUE X"09". 05 CX922 PIC S9(03) SIGN LEADING VALUE 121. 05 FILLER PIC X(01) VALUE X"09". 05 CX923 PIC S9(03) SIGN LEADING VALUE 123. 05 FILLER PIC X(01) VALUE X"09". 05 CX924 PIC S9(03) SIGN LEADING VALUE 129. 05 FILLER PIC X(01) VALUE X"09". 05 CX925 PIC S9(03) SIGN LEADING SEPARATE VALUE 120. 05 FILLER PIC X(01) VALUE X"09". 05 CX926 PIC S9(03) SIGN LEADING SEPARATE VALUE 121. 05 FILLER PIC X(01) VALUE X"09". 05 CX927 PIC S9(03) SIGN LEADING SEPARATE VALUE 123. 05 FILLER PIC X(01) VALUE X"09". 05 CX928 PIC S9(03) SIGN LEADING SEPARATE VALUE 129. 05 FILLER PIC X(01) VALUE X"09". 01 MY-DATA-3. 05 CX931 PIC S9(03) SIGN TRAILING VALUE 120. 05 FILLER PIC X(01) VALUE X"09". 05 CX932 PIC S9(03) SIGN TRAILING VALUE 121. 05 FILLER PIC X(01) VALUE X"09". 05 CX933 PIC S9(03) SIGN TRAILING VALUE 123. 05 FILLER PIC X(01) VALUE X"09". 05 CX934 PIC S9(03) SIGN TRAILING VALUE 129. 05 FILLER PIC X(01) VALUE X"09". 05 CX935 PIC S9(03) SIGN TRAILING SEPARATE VALUE 120. 05 FILLER PIC X(01) VALUE X"09". 05 CX936 PIC S9(03) SIGN TRAILING SEPARATE VALUE 121. 05 FILLER PIC X(01) VALUE X"09". 05 CX937 PIC S9(03) SIGN TRAILING SEPARATE VALUE 123. 05 FILLER PIC X(01) VALUE X"09". 05 CX938 PIC S9(03) SIGN TRAILING SEPARATE VALUE 129. 05 FILLER PIC X(01) VALUE X"09". 01 MY-DATA-4. 05 CX941 PIC S9(03) SIGN LEADING VALUE -120. 05 FILLER PIC X(01) VALUE X"09". 05 CX942 PIC S9(03) SIGN LEADING VALUE -121. 05 FILLER PIC X(01) VALUE X"09". 05 CX943 PIC S9(03) SIGN LEADING VALUE -123. 05 FILLER PIC X(01) VALUE X"09". 05 CX944 PIC S9(03) SIGN LEADING VALUE -129. 05 FILLER PIC X(01) VALUE X"09". 05 CX945 PIC S9(03) SIGN LEADING SEPARATE VALUE -120. 05 FILLER PIC X(01) VALUE X"09". 05 CX946 PIC S9(03) SIGN LEADING SEPARATE VALUE -121. 05 FILLER PIC X(01) VALUE X"09". 05 CX947 PIC S9(03) SIGN LEADING SEPARATE VALUE -123. 05 FILLER PIC X(01) VALUE X"09". 05 CX948 PIC S9(03) SIGN LEADING SEPARATE VALUE -129. 05 FILLER PIC X(01) VALUE X"09". 01 MY-DATA-5. 05 CX951 PIC S9(03) SIGN TRAILING VALUE -120. 05 FILLER PIC X(01) VALUE X"09". 05 CX952 PIC S9(03) SIGN TRAILING VALUE -121. 05 FILLER PIC X(01) VALUE X"09". 05 CX953 PIC S9(03) SIGN TRAILING VALUE -123. 05 FILLER PIC X(01) VALUE X"09". 05 CX954 PIC S9(03) SIGN TRAILING VALUE -129. 05 FILLER PIC X(01) VALUE X"09". 05 CX955 PIC S9(03) SIGN TRAILING SEPARATE VALUE -120. 05 FILLER PIC X(01) VALUE X"09". 05 CX956 PIC S9(03) SIGN TRAILING SEPARATE VALUE -121. 05 FILLER PIC X(01) VALUE X"09". 05 CX957 PIC S9(03) SIGN TRAILING SEPARATE VALUE -123. 05 FILLER PIC X(01) VALUE X"09". 05 CX958 PIC S9(03) SIGN TRAILING SEPARATE VALUE -129. 05 FILLER PIC X(01) VALUE X"09". 01 MY-DATA-6. 05 CX951 PIC S9(03) SIGN TRAILING VALUE +0. 05 FILLER PIC X(01) VALUE X"09". 05 CX952 PIC S9(03) SIGN TRAILING VALUE +1. 05 FILLER PIC X(01) VALUE X"09". 05 CX953 PIC S9(03) SIGN TRAILING VALUE -0. 05 FILLER PIC X(01) VALUE X"09". 05 CX954 PIC S9(03) SIGN TRAILING VALUE -1. 05 FILLER PIC X(01) VALUE X"09". 05 CX955 PIC S9(03) SIGN TRAILING SEPARATE VALUE +0. 05 FILLER PIC X(01) VALUE X"09". 05 CX956 PIC S9(03) SIGN TRAILING SEPARATE VALUE +1. 05 FILLER PIC X(01) VALUE X"09". 05 CX957 PIC S9(03) SIGN TRAILING SEPARATE VALUE -0. 05 FILLER PIC X(01) VALUE X"09". 05 CX958 PIC S9(03) SIGN TRAILING SEPARATE VALUE -1. 05 FILLER PIC X(01) VALUE X"09". PROCEDURE DIVISION. MAIN. OPEN OUTPUT PRINTF. MOVE SPACES TO DATA-PRINT. MOVE MY-DATA-0 TO DATA-PRINT. WRITE PRINT-REC. MOVE SPACES TO DATA-PRINT. MOVE MY-DATA-1 TO DATA-PRINT. WRITE PRINT-REC. MOVE SPACES TO DATA-PRINT. MOVE MY-DATA-2 TO DATA-PRINT. WRITE PRINT-REC. MOVE SPACES TO DATA-PRINT. MOVE MY-DATA-3 TO DATA-PRINT. WRITE PRINT-REC. MOVE SPACES TO DATA-PRINT. MOVE MY-DATA-4 TO DATA-PRINT. WRITE PRINT-REC. MOVE SPACES TO DATA-PRINT. MOVE MY-DATA-5 TO DATA-PRINT. WRITE PRINT-REC. MOVE SPACES TO DATA-PRINT. MOVE MY-DATA-6 TO DATA-PRINT. WRITE PRINT-REC. CLOSE PRINTF. DISPLAY "Press anykey to continue ..." . ACCEPT ANS. EXIT PROGRAM. STOP RUN.
沒有留言:
張貼留言