הידע המינימלי הנדרש לפני קריאת קוד COBOL
· עודכן בתאריך: · Go Komura · COBOL, טכנולוגיה ישנה, מערכות עסקיות, תחזוקה, Mainframe
העברת אחריות, טיפול בתקלה, תחזוקת חבילה של ספק. במצבים כאלה, יכול לקרות שיום אחד פתאום מגיע אליכם קוד COBOL.
- שם הקובץ
.cblאו.cpy - שמות המשתנים כולם באותיות גדולות
-
01,05,77,88מסודרים בשורה - מופיע רישום כמו
PIC S9(7)V99 COMP-3, משהו בין לחש קסמים לתוכנת הנהלת חשבונות - ובנוסף, הכול מלא ב-
COPY, ורק מהקובץ הפתוח לא רואים את התמונה השלמה
בשלב הזה, רוב האנשים עוצרים לרגע.
עם זאת, המפה הדרושה לקריאה לא כל כך גדולה. יש הבדלים בין מערכות ומוצרי COBOL שונים, אבל השלד שכדאי לתפוס קודם כשקוראים מערכת עסקית קיימת די משותף. המאמר הזה מסדר, על בסיס 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. אם יודעים לקרוא את אלה, שיעור ההיאבדות יורד משמעותית.
מפת הידע של המאמר
COBOL היא, לפני שהיא שפת סדר פעולות דרך PROCEDURE DIVISION, שפה שמגדירה ב-DATA DIVISION את צורת הרשומה, כשכל פריט נתונים מציין את הצורה שלו עם סעיף PICTURE ואת הייצוג הפנימי כמו COMP-3 עם סעיף USAGE. COMP-3 הוא packed decimal שדוחס ספרה עשרונית שתיים לבייט, ונראה כרצף בייטים חסר משמעות אם פותחים אותו כטקסט. REDEFINES מפרש מחדש את אותו שטח בצורה אחרת, OCCURS מייצג מערך, ורמה 88 מייצגת תנאי ששמו ניתן לערך של הפריט שקדם לו ישירות. משפט COPY מכניס copybook חיצוני בזמן קומפילציה, ולכן הקובץ הפתוח כשלעצמו לא מראה את התמונה השלמה, ויש צורך לעקוב אחרי זרימת העיבוד עם משפט PERFORM ו-scope terminator, ולתפוס את הגבול החיצוני עם FILE STATUS, EXEC SQL ו-EXEC CICS.
flowchart LR
accTitle: מפת הידע של קריאת COBOL
accDescr: תרשים שמראה ש-COBOL, לפני שהיא שפת סדר פעולות דרך PROCEDURE DIVISION, היא שפה שמגדירה את צורת הרשומה ב-DATA DIVISION, ואת הקשרים בין סעיף PICTURE, USAGE, COMP-3, REDEFINES, OCCURS, רמה 88, משפט COPY, משפט PERFORM ו-scope terminator, ואת גבולות החוץ כמו EBCDIC, FILE STATUS, EXEC SQL ו-EXEC CICS.
cobol["COBOL"]
cobol_data_division["DATA DIVISION"]
comp_3["COMP-3(packed decimal)"]
cobol_procedure_division["PROCEDURE DIVISION"]
cobol_picture_clause["סעיף PICTURE (PIC)"]
cobol_usage_clause["סעיף USAGE"]
comp_5["COMP-5"]
cobol_redefines["סעיף REDEFINES"]
cobol_occurs["סעיף OCCURS"]
cobol_occurs_depending_on["OCCURS DEPENDING ON"]
cobol_88_level["רמה 88 (שם תנאי)"]
cobol_copy["משפט COPY"]
copybook["copybook"]
cobol_perform["משפט PERFORM"]
cobol_scope_terminator["מסיים תחום (scope terminator)"]
cobol_move["משפט MOVE"]
cobol_fixed_format["פורמט קבוע (פורמט ייחוס)"]
ibm_enterprise_cobol["IBM Enterprise COBOL"]
ebcdic["EBCDIC"]
cobol_file_status["סעיף FILE STATUS"]
exec_sql["EXEC SQL"]
exec_cics["EXEC CICS"]
cobol -->|"משתמש ב"| cobol_data_division
cobol -->|"משתמש ב"| cobol_procedure_division
cobol_data_division -->|"משתמש ב"| cobol_picture_clause
cobol_data_division -->|"משתמש ב"| cobol_usage_clause
cobol_usage_clause -->|"משתמש ב"| comp_3
cobol_usage_clause -->|"משתמש ב"| comp_5
cobol_data_division -->|"משתמש ב"| cobol_redefines
cobol_data_division -->|"משתמש ב"| cobol_occurs
cobol_occurs_depending_on -->|"משתמש ב"| cobol_occurs
cobol_data_division -->|"משתמש ב"| cobol_88_level
cobol -.->|"משתמש ב"| cobol_copy
cobol_copy -->|"משתמש ב"| copybook
cobol_procedure_division -->|"משתמש ב"| cobol_perform
cobol_procedure_division -->|"משתמש ב"| cobol_scope_terminator
cobol_procedure_division -->|"משתמש ב"| cobol_move
cobol -.->|"משתמש ב"| cobol_fixed_format
ibm_enterprise_cobol -->|"משתמש ב"| ebcdic
ibm_enterprise_cobol -.->|"משתמש ב"| cobol_fixed_format
cobol -.->|"משתמש ב"| cobol_file_status
cobol -.->|"משתמש ב"| exec_sql
cobol -.->|"משתמש ב"| exec_cics
בתרשים, קו מלא מציין קשר שמתקיים תמיד וקו מקווקו מציין קשר מותנה (תנאי ההתקיימות מפורטים בהסבר של כל קשר בעמוד המפורט). רשימת כל הקשרים (סך הכול 21, עם אסמכתה ורמת ודאות) והגדרות המושגים המרכזיים מרוכזות בעמוד המפורט של מפת הידע (ביפנית). נתונים: JSON-LD / Turtle
2. חושבים על COBOL קודם כשפת “צורת הנתונים”
אם קוראים בתחושה של C# או Java, בהתחלה רוצים לעקוב אחרי if, for וקריאות לפונקציות.
אבל ב-COBOL, לפני זה, מהיר יותר לתפוס “איזו רשומה התכנית הזו מקבלת, איזו רשומה היא יוצרת, ואיזה buffer היא מחזיקה”.
קוד COBOL עסקי אופייני הולך בערך בזרימה הזו.
- קוראים רשומה מקובץ או מ-DB
- מכניסים לפריט ב-
WORKING-STORAGE - מסתעפים בתנאי
- ממלאים לתוך רשומה אחרת
- כותבים החוצה
כלומר, הפריסה נוטה להקדים את האלגוריתם.
flowchart TB
accTitle: הזרימה האופיינית ל-COBOL עסקי
accDescr: תרשים שמראה שקוד COBOL עסקי אופייני קורא רשומה מקובץ או DB, מכניס אותה לפריט ב-WORKING-STORAGE, מסתעף בתנאי, ממלא לתוך רשומה אחרת וכותב אותה החוצה.
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. קודם מסתכלים על 4 ה-DIVISION
קוד COBOL מתחלק תחילה, בגדול, ל-4 DIVISION.
| DIVISION | מה בודקים תחילה |
|---|---|
IDENTIFICATION DIVISION |
שם התכנית, הערות ישנות, מקור |
ENVIRONMENT DIVISION |
קבצים, משאבים חיצוניים, הנחות קלט/פלט |
DATA DIVISION |
הגדרת רשומות, שטח עבודה, ארגומנטים |
PROCEDURE DIVISION |
הליך העיבוד עצמו |
בפרט חשובים אלה.
-
FILE SECTIONיש הגדרת רשומה של קבצי קלט/פלט -
WORKING-STORAGE SECTIONיש משתנים, דגלים, מונים, buffer עבודה בשימוש רגיל -
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 -
*>: הערה שאפשר לכתוב גם באמצע השורה
ההתאמה בין עמודות לתוכן, עם סרגל, נראית כך. שורה ראשונה - ספרת העשרות, שורה שנייה - ספרת האחדות.
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'.
ארבע נקודות שכדאי לשים לב אליהן בקריאה.
- עמודות 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, ואם מפעילים עיצוב אוטומטי מודרני או המרת tab על קוד בפורמט fixed, הגבול בין 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. נותן שם לערך של הפריט שקדם לו ישירות3 -
66: לשימושRENAMES. לא נפוץ מאוד, אבל קיים
חשוב לא לחשוב על 88 כמשתנה בוליאני נפרד.
זה לא ששטח בשם WS-OK קיים בנפרד, אלא שכש-WS-STATUS הוא '0', אפשר לקרוא לזה בשם WS-OK.
flowchart TB
accTitle: הזהות של רמה 88
accDescr: תרשים שמראה שרמה 88 היא לא משתנה בוליאני עצמאי, אלא שם תנאי שניתן לערך של הפריט שקדם לו ישירות, וכש-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 ספרות אחרי הנקודה”, אבל בנתונים עצמם אין תו נקודה.
לכן, אם קוראים קובץ או dump כ”מחרוזת שנראית”, לרוב זה נכשל.
flowchart TB
accTitle: V היא נקודה עשרונית לוגית
accDescr: תרשים שמראה של-V ב-PIC 9(5)V99 אין תו נקודה ממשי, זה מטופל כמספר עם 2 ספרות אחרי הנקודה אבל אין נקודה בנתונים, ולכן אם קוראים קובץ או dump כמחרוזת שנראית, זה נכשל.
vd1["PIC 9(5)V99"] --> vd2["מטופל כמספר עם 2 ספרות עשרוניות"]
vd2 --> vd3["אין תו נקודה בנתונים"]
vd3 -.-> vd4["פרשנות כמחרוזת שנראית נכשלת"]
איור 6: הנקודה קיימת רק בהגדרה, ולא בנתונים.
5.3 USAGE / DISPLAY / COMP / COMP-3
אם PIC הוא הצורה, USAGE הוא באיזה ייצוג נשמר.
מספיק לתפוס לפחות את אלה כדי לקרוא ברוב המקרים.45
| רישום | משמעות בגסות | הערה בקריאה |
|---|---|---|
DISPLAY |
עשרוני חיצוני שנראה כתו | ב-mainframe לפעמים מניח EBCDIC6 |
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.
שלושת אלה כולם “מספרים”, אבל הצורה הפנימית שלהם שונה.
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, זו תקלה
אם מחזיקים הבנה כזו, אפשר להימנע מלהיבהל סתם מהמראה של dump או קובץ בינארי.
flowchart TB
accTitle: התגובה ברגע שרואים COMP-3
accDescr: תרשים שמראה שכשרואים COMP-3, מבינים שזה packed decimal, כנראה שדה של סכום, מס, מספר פריטים או תעריף, שטבעי לגמרי שנראה שבור כטקסט, ולא נבהלים כשמסתכלים בתחושת CSV או UTF-8.
cp1["מוצאים COMP-3"] --> cp2["מבינים שזה packed decimal"]
cp2 --> cp3["חושדים בשדה כספי או מספר פריטים"]
cp3 --> cp4["לא נבהלים גם אם נראה שבור כטקסט"]
איור 8: רק הרפלקס הזה מקטין את מספר הפעמים שנבהלים מול dump.
איך COMP-3 נראה בפועל כרצף בייטים
שווה לעבור על זה פעם אחת ידנית, וזה משנה את המראה מכאן ואילך.
לחוקי ה-packed decimal יש רק שני כללים.45
- ספרה עשרונית נדחסת שתיים בכל בייט
- אבל, רק הבייט הימני ביותר משתמש לספרה התחתונה ולסימן
הסימן מיוצג בערך בן 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 נוצר רווח בגובה ספרה אחת בהתחלה. זה ה-0 שבתחילה.
באותה שיטה, בואו נעקוב אחר PIC S9(7)V99 COMP-3 שהופיע כמה פעמים במאמר הזה.
-
S9(7)V99הוא 7 ספרות שלמות + 2 ספרות עשרוניות = 9 ספרות -
Vרק מציין את מיקום הנקודה העשרונית, ולכן הוא לא צורך אף בייט - אם דוחסים 9 ספרות שתיים לבייט, זה 4 בייטים, והספרה שנותרת + הסימן הם עוד בייט. סך הכול 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 לא מופיע בשום מקום.
זו הזהות של “נראה כמו מוג’יבאקה אבל לא שבור”. אם פותחים dump ונתקלים ברצף לא מובן, כדאי לחשוד קודם שה-USAGE של הפריט הזה הוא COMP-3.
flowchart TB
accTitle: איך רצף הבייטים של COMP-3 נבנה
accDescr: תרשים שמראה שב-packed decimal דוחסים ספרה עשרונית שתיים לבייט, רק הבייט האחרון משמש לספרה התחתונה ולסימן, ולכן מספר בן 9 ספרות הופך ל-5 בייטים, ואם פותחים את זה כ-ASCII הרצף המקורי לא מופיע בשום מקום.
pk1["דוחסים ספרה עשרונית שתיים לבייט"] --> pk2["הבייט האחרון הוא ספרה תחתונה + סימן"]
pk2 --> pk3["9 ספרות הופכות ל-5 בייטים"]
pk3 --> pk4["פתיחה כ-ASCII לא מראה את המספר המקורי"]
pk4 -.-> pk5["נראה כמו מוג'יבאקה אבל לא שבור"]
איור 9: אם יודעים את שני כללי הדחיסה, dump לא מובן הופך לרצף שאפשר לקרוא.
בנוסף, הנה גם טבלת עזר מהירה שממירה בין מספר ספרות למספר בייטים. מספר הבייטים מתקבל לפי “מחלקים את מספר ה-9 בשתיים, מעגלים כלפי מטה, ומוסיפים 1”.
| מספר ה-9 ב-PICTURE | מספר בייטים ב-COMP-3 |
|---|---|
| 1 | 1 |
| 2-3 | 2 |
| 4-5 | 3 |
| 6-7 | 4 |
| 8-9 | 5 |
| 10-11 | 6 |
| 12-13 | 7 |
הסיבה שאותו מספר בייטים מתקבל משני מספרי ספרות היא שכשמספר ה-9 זוגי, נוצר רווח של ספרה אחת בתחילה. זו הסיבה ש-S9(4) הופך ל-3 בייטים כמו 01 23 4C, וגם S9(5) נכנס לאותם 3 בייטים.
בהתאמת פריסה מול קובץ חיצוני, בלי הטבלה הזו, ההיסט זז בייט אחד בכל פעם.
עוד הערה קטנה: זה שמדובר ב-DISPLAY לא בהכרח אומר מחרוזת ASCII.
במערכות מסוג z/OS, מניחים EBCDIC, ולכן גם אם ספרות נראות כתו, ערך הבייט שונה מ-'0'-'9' של ASCII.6
5.4 REDEFINES / OCCURS / COPY / FILLER
ארבעת אלה הם המקומות שהכי קל להיתקע בהם בקריאה.
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).
זה קרוב לתחושה של union בשפות מסוג C.
מופיע הרבה בכתיבה מהסוג “מזהים 100 בייט אחד כסוגי רשומה שונים”.
flowchart TB
accTitle: REDEFINES היא פרשנות אחרת לאותו שטח
accDescr: תרשים שמראה ש-REDEFINES היא לא העתקה אלא מנגנון שרואה את אותו שטח זיכרון בצורה אחרת, כשאותו buffer נראה גם כרשומה כללית וגם כרשומת header, וכתיבה בצד אחד משנה את המראה בצד השני.
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, זו טבלה באורך משתנה.
במקרה כזה, זה עשוי להשפיע גם על המיקום של הפריטים הבאים, ולכן אם עוקבים בתחושת אורך קבוע, מפספסים.8
flowchart TB
accTitle: תשומת לב ל-OCCURS DEPENDING ON
accDescr: תרשים שמראה ש-OCCURS הוא מערך, ואם מופיע DEPENDING ON זו הופכת לטבלה באורך משתנה שהמיקום של הפריטים הבאים עשוי לזוז בהתאם לערך, ולכן עקיבה בתחושת אורך קבוע מפספסת.
oc1["מוצאים OCCURS"] --> oc2{"יש DEPENDING ON?"}
oc2 -->|"אין"| oc3["טבלה במספר קבוע"]
oc2 -->|"יש"| oc4["טבלה באורך משתנה"]
oc4 -.-> oc5["המיקום של הפריטים הבאים עשוי לזוז"]
איור 11: שלוש המילים DEPENDING ON משנות את הנחת חישוב ההיסט.
COPY
COPY הוא include בזמן קומפילציה.
כלומר, הקובץ הפתוח כרגע עשוי עדיין לא להיות הצורה המלאה.9
COPY CUSTOMER-REC.
COPY ERROR-MAP.
די רגיל שהגדרת רשומה, דגלים משותפים, host variable ל-SQL, וממשק חיצוני דחוסים בתוך ה-copybook.
כשיש הרבה COPY וקשה לקרוא, מהיר יותר לבדוק אם אפשר לראות את המקור אחרי הרחבה או את ה-compiler listing. ל-IBM Enterprise COBOL יש גם אפשרות בשם MDECK, שמוציאה את מקור הקלט אחרי עיבוד הספרייה.10
flowchart TB
accTitle: איך קוראים קוד עם COPY
accDescr: תרשים שמראה ש-COPY הוא include בזמן קומפילציה ולכן הקובץ הפתוח עשוי להיות לא שלם, ולכן פותחים את ה-copybook לבדיקה, ואם קשה לקרוא, מחפשים את המקור אחרי הרחבה, 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 יש בגדול שתי משפחות.
- out-of-line
PERFORMשמציין פסקה או section - inline
PERFORM ... END-PERFORMשכותב את הבלוק במקום
בקוד ישן עוד יותר, מופיע גם ציון טווח רגיל כמו PERFORM A-100 THRU A-199.
זה נוח, אבל אם מוסיפים פסקה באמצע, קל שזה יגרור תקלה נלווית, ולכן בקריאה בודקים היטב את סוף הטווח.
flowchart TB
accTitle: שלוש הצורות של PERFORM
accDescr: תרשים שמראה של-PERFORM יש out-of-line שקורא פסקה או section וחוזר, 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-IF -
END-PERFORM -
END-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 עסקי.
-
READ -
WRITE -
REWRITE -
START -
CALL
בפרט 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 של הצד הנקרא ועל ה-USING שלו, רואים את צורת העברת הארגומנטים.
ca1["מוצאים CALL"] --> ca2["קופץ לתכנית אחרת"]
ca2 --> ca3["מסתכלים על LINKAGE SECTION של הנקרא"]
ca3 --> ca4["ה-USING מראה את צורת ההעברה"]
איור 16: משמעות הקריאה נקראת יחד עם כניסת הצד הנקרא.
7. מה שנמצא מחוץ ל-COBOL
יש הרבה מקרים שבהם ה-COBOL עצמו לא מכיל את כל העולם.
- הגדרת קובץ
- סביבת הרצה
- חיבור ל-DB
- הקשר טרנזקציה
- בקרת job
כי אלה מחולקים החוצה.
לפחות אלה כדאי לתפוס כדי שהקריאה תהיה קלה יותר.
קובץ ו-FILE STATUS
קוראים יחד את FILE-CONTROL ב-ENVIRONMENT DIVISION ואת FILE SECTION / FD ב-DATA DIVISION.13
SELECT IN-FILE ASSIGN TO ...
FILE STATUS IS WS-FS.
FD IN-FILE.
01 IN-REC.
05 ...
אם יש FILE STATUS, נכנס לשם קוד תוצאה אחרי כל קלט/פלט.
בקריאת תקלות קובץ או בדיקת EOF, בלי להסתכל על זה לא מתחילים בכלל.14
flowchart TB
accTitle: הגדרת קובץ נקראת יחד
accDescr: תרשים שמראה ש-SELECT תחת FILE-CONTROL ו-FD תחת FILE SECTION נקראים יחד כהגדרת קובץ אחת, ואם יש FILE STATUS נכנס לשם קוד תוצאה אחרי כל קלט/פלט, שמשמש נקודת מוצא לקריאת תקלה ובדיקת EOF.
fs1["SELECT תחת FILE-CONTROL"] --> fs3["יחד - הגדרת קובץ אחת"]
fs2["FD תחת FILE SECTION"] --> fs3
fs3 --> fs4["FILE STATUS מקבל קוד תוצאה"]
fs4 -.-> fs5["נקודת מוצא לקריאת תקלה ו-EOF"]
איור 17: צורת הקובץ מחולקת לכתיבה בשני DIVISION.
EXEC SQL
אם זה מופיע, זה SQL משובץ.
EXEC SQL
SELECT ...
END-EXEC.
במקרה כזה, ה-COBOL הוא “כלי קיבול למשתני host”, ותנאי השליפה או יעד העדכון האמיתיים נמצאים בצד ה-SQL.
לכן, לקרוא את תוכן ה-EXEC SQL כ-SQL רגיל זו הדרך הקצרה.
EXEC CICS
אם זה מופיע, זה הקשר טרנזקציה של CICS.15
EXEC CICS
RECEIVE MAP(...)
END-EXEC.
ברגע הזה, זה כבר לא סתם קריאת batch. נדרש לקרוא כולל הקשר חיצוני - מסך, טרנזקציה, קוד תגובה, COMMAREA.
JCL או הגדרות הרצה
ב-mainframe batch, לא נדיר שאיזה dataset בפועל מוקצה או באיזה סדר ה-job זורם נמצא מחוץ לקוד ה-COBOL. כשמסתכלים רק על הקוד ולא רואים “איפה הקובץ הזה נמצא”, זה לא בהכרח בעיה בקוד, אלא לעיתים סתם שהטווח שרואים עדיין לא מספיק.
flowchart TB
accTitle: העולם שמחוץ לקוד
accDescr: תרשים שמראה שאם מופיע EXEC SQL, תנאי השליפה בפועל נמצאים בצד ה-SQL, אם מופיע EXEC CICS, ההקשר החיצוני כולל מסך וטרנזקציה, וב-mainframe batch הקצאת dataset וזרימת job נמצאים ב-JCL - כלומר יש עולם מחוץ לקוד ה-COBOL.
ex1["EXEC SQL"] --> ex7["מסתכלים גם מחוץ לקוד"]
ex3["EXEC CICS"] --> ex7
ex5["JCL או הגדרות הרצה"] --> ex7
ex7 -.-> ex2["תנאי SQL, הקשר מסך, זרימת job"]
איור 18: לפעמים אי-ההבנה היא לא באשמת הקוד, אלא רק כי הטווח שרואים צר מדי.
הבדל בין מערכות (מהדרים)
המאמר הזה נכתב מתוך התייחסות למשפחת IBM, אבל בפועל אפשר להיתקל גם ב-Micro Focus, או COBOL על Linux / Windows. השלד משותף, ולכן אופן הקריאה לא משתנה, אבל יש נקודות קבועות שבהן “אותה כתיבה, תוצאה שונה”, ולכן כדאי לתפוס אותן מראש כדי לא להיכשל.
| מה בודקים | משפחת z/OS (IBM Enterprise COBOL) | משפחת open (Micro Focus, גרסת Linux / Windows וכדומה) |
|---|---|---|
| קידוד תווים | מניח EBCDIC6 | מניח ASCII6 |
| פורמט ייחוס | פורמט fixed הוא ברירת המחדל המסורתית. אפשר לבחור גם free1 | יש גם 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, אופן קריאת ה-dump משתנה.
- מסמנים פריטים ממשפחת
COMP. כמעט תמיד כאן מופיעים הבדלי מערכת.
flowchart TB
accTitle: הצעדים הקצרים ביותר להשוואה מול הסביבה שלכם
accDescr: תרשים שמראה שקודם פותחים את הגדרת הבנייה כדי לדעת באיזו מערכת ובאילו אפשרויות מקומפלים, קובעים את פורמט הייחוס fixed או free, קובעים את קידוד התווים EBCDIC או ASCII, ומסמנים פריטים ממשפחת COMP שבהם נוטים להופיע הבדלי מערכת.
ck1["פותחים קודם הגדרת בנייה"] --> ck2["קובעים פורמט ייחוס"]
ck2 --> ck3["קובעים קידוד תווים"]
ck3 --> ck4["מסמנים פריטים ממשפחת COMP"]
איור 19: ב-4 הצעדים האלה לפני קריאת הקוד, נמנעים מתקלות הבדלי מערכת.
8. סדר הקריאה המינימלי
כשפתאום נאלצים לקרוא COBOL, הסדר הבא בטוח.
- עוברים על כל ה-
COPYאם אפשר לפתוח copybook, פותחים. אם לא, מחפשים listing או מקור אחרי הרחבה - אוספים הגדרות רשומה ברמת
01מרכזים רשימה של הרמה העליונה שלFILE SECTION,WORKING-STORAGE,LINKAGE SECTION - קוראים
PICו-USAGEמזהים סכום, תאריך, מספר פריטים, קוד, דגל - מחפשים
READ/WRITE/REWRITE/CALL/EXEC SQL/EXEC CICSתופסים קודם קלט/פלט וגבול חיצוני - עוקבים רק אחרי המסלול הראשי הראשון
עוקבים אחרי שרשרת ה-
PERFORMמתחילתPROCEDURE DIVISION - מסתכלים על
88ופריטי status המשמעות של EOF, תקין/לא תקין, קוד סוג נעשית קלה יותר לקריאה - מסמנים
REDEFINES/OCCURS DEPENDING ON/COMP-3אלה תמיד ישפיעו בהמשך, ולכן מסמנים אותם מראש כחומר מסוכן - בקובץ, מסתכלים על
FILE STATUSזה מקטין משמעותית טעויות קריאה בתחום שגיאות קלט/פלט
בסדר הזה, לא צריך לקרוא בעיון את כל הטקסט מיד. עדיף לתפוס קודם את שלוש הנקודות רשומה, גבול חיצוני, מסלול ראשי, ורק אז לרדת לפרטים - זה הרבה יותר קל מלנסות להבין 100% מההתחלה.
flowchart TB
accTitle: סדר קריאה בטוח
accDescr: תרשים שמראה שעוברים על ה-COPY ובודקים את ה-copybook, מרכזים רשימת הגדרות רשומה ברמת 01, קוראים את צורת הפריטים דרך PIC ו-USAGE, מחפשים קלט/פלט וגבול חיצוני, עוקבים אחרי שרשרת PERFORM ראשית מ-PROCEDURE DIVISION, ומסמנים חומר מסוכן.
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? - מאיזה בייט מתחיל
HIST-AMOUNTשל הרשומה השנייה, נספר מתחילת הרשומה? - כש-
CUST-KBNמכיל'1', איזה שם תנאי הופך לנכון? - אם פותחים את הקובץ הזה בעורך טקסט, איזה פריט נראה שבור?
- ציינו שני דברים שאי אפשר לדעת רק מההגדרה הזו.
פתרונות
-
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, ופריט אחד הוא 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'. מוגדרים רק שני שמות תנאי, אבל זה לא מבטיח שלא יגיע ערך אחר. - תכונות הקובץ עצמו (אורך רשומה, האם משתנה אורך,
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 הוא מיקום קבוע
הפריטים שאחרי טבלה באורך משתנה עשויים לזוז במיקום לפי הערך. אם קוראים בראש של אורך קבוע, כל חישוב ההיסט זז.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 |
קלט/פלט לקובץ |
EXEC SQL |
עיבוד DB |
EXEC CICS |
עיבוד טרנזקציה |
FILE STATUS |
קוד תוצאה של קלט/פלט |
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 וקלט/פלט, ותופסים את הגבול החיצוני עם 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 節” ↩ ↩2
-
IBM, “OCCURS DEPENDING ON clause” ↩ ↩2
-
IBM, “COPY ステートメント” ↩ ↩2
-
IBM, “PERFORM statement” / IBM, “Procedure division structure” ↩
-
IBM, “Scope terminators” / IBM, “Coding a choice of actions” ↩ ↩2 ↩3 ↩4
-
IBM, “ファイル構造の詳細記述” ↩
-
IBM, “FILE STATUS clause” / IBM, “Using file status keys” ↩
-
IBM, “Computational items” ↩
-
IBM, “Elementary move rules” ↩
מאמרים קשורים
מאמרים עדכניים עם אותן תגיות, להעמקה בנושאים קרובים.
מה זה Reg-Free COM - שימוש ב-COM בלי רישום
המאמר מסדר את היסודות של Reg-Free COM - תפקיד ה-activation context וה-manifest, היתרונות, המגבלות, וקריטריוני ההחלטה בפועל.
להופיע בחיפושים עם שם האזור — מדריך מעשי ל-SEO מקומי לעסקים קטנים ובינוניים (עמודי אזורים ו-Google Business Profile)
לעסקים קטנים ובינוניים שהאתר שלהם לא מופיע כשמחפשים "שם אזור + תחום". המאמר מסדר את סדר הפעולות ב-SEO מקומי: הגדרת Google Business Profil...
רשימת בדיקה לטיפול בטוח בתהליכי ילד ביישום Windows
כדי לטפל בבטחה בתהליכי ילד ביישום Windows, תכנון הבעלות על עץ התהליכים ונוהל הסיום חשוב יותר מבחירת ה-API להפעלה. המאמר מסדר את Job Objec...
איך בוחרים שיטת הפצה ליישום Windows - MSI/MSIX/ClickOnce/xcopy/עדכון עצמי
בחירת שיטת ההפצה ליישום Windows אינה עניין של טעם בצורת המתקין, אלא בחירה של מידת הצימוד ל-OS ושל מי נושא באחריות העדכון. המאמר מסדר את M...
הפצת יישום Windows בקובץ אחד - הגבול בין בינארי יחיד לתלות במערכת ההפעלה
כשרוצים לרכז יישום Windows ל-EXE אחד, המאמר מסדר את ההבדל בין ריכוז ההפצה לפריט אחד לבין ביטול התלות במערכת ההפעלה, וכולל .NET, C++, W...
נושאים קשורים
העמודים האלה ממקמים את הנושא בהקשר רחב יותר של שירותים והחלטות.
נושאים טכניים ב-Windows
שער לנושאי פיתוח Windows, חקירת תקלות וניצול נכסים קיימים.
שירותים הקשורים לנושא הזה
המאמר קשור ישירות לשירותים הבאים.
ייעוץ טכני וסקירת תכנון
איך קוראים נכסי COBOL קיימים, נקודת הכניסה לשינוי, הבנת גבולות חיצוניים, וסידור הערכה לפני מעבר - כל אלה נושאים שמתאימים היטב לייעוץ טכני ולסקירת תכנון.
חקירת תקלות ואיתור גורמים
טיפול בתקלה מיד אחרי קבלת אחריות, או מעקב אחרי המקום שבו נכסי COBOL לא עקביים - זו עבודה שקל להתקדם בה כחקירת תקלות וניתוח שורש.
שאלות נפוצות
שאלות נפוצות בפניות בנושא המאמר.
- מאיפה כדאי להתחיל לקרוא קוד COBOL?
- קריאה של PROCEDURE DIVISION בלבד נותנת רק חצי מהתמונה. COBOL היא, לפני שהיא שפת לוגיקה, שפה שמתמקדת חזק מאוד בהגדרת רשומות, ולכן קודם כל בודקים את DATA DIVISION. סדר קריאה בטוח: לעבור על כל ה-COPY ולבדוק את ה-copybook, לרכז רשימה של הגדרות הרשומה ברמה 01, לקרוא את צורת הפריטים דרך PIC ו-USAGE, לחפש READ, WRITE, CALL, EXEC SQL, EXEC CICS כדי לתפוס קלט/פלט וגבול חיצוני, ורק אז לעקוב אחרי שרשרת ה-PERFORM בתחילת PROCEDURE DIVISION כדי לתפוס את המסלול הראשי.
- מה המשמעות של PIC S9(7)V99 COMP-3?
- PIC מציין את צורת הפריט, ו-USAGE מציין באיזה ייצוג הוא נשמר. S9(7)V99 הוא מספר עם סימן, 7 ספרות שלמות + 2 ספרות עשרוניות, אבל ה-V הוא נקודה עשרונית לוגית בלבד - אין תו נקודה ממשי בנתונים. COMP-3 הוא packed decimal, ומופיע הרבה בפריטים של סכומים, מסים, מספר פריטים, ותעריפים. כשמסתכלים על זה כטקסט זה נראה שבור, וזה טבעי לגמרי, ולכן אסור להסתכל על dump בתחושה של CSV או UTF-8.
- איך מבינים את רמת 88 ו-REDEFINES ב-COBOL?
- רמה 88 היא לא משתנה בוליאני עצמאי, אלא שם תנאי (condition-name) שנותנים לערך של הפריט שקדם לו ישירות. SET WS-OK TO TRUE מכניס מאחורי הקלעים את הערך המתאים לפריט הבסיס. REDEFINES הוא מנגנון שמסתכל על אותו שטח זיכרון בצורה אחרת - זה לא העתקה, אלא תחושה קרובה ל-union בשפות מסוג C. אם משנים צד אחד, גם המראה של הצד השני משתנה, ולכן זה מופיע הרבה בכתיבה שמזהה את אותו שטח לפי סוג הרשומה.
- מה עושים כשיש הרבה משפטי COPY ולא רואים את התמונה השלמה?
- COPY הוא include בזמן קומפילציה, ולכן ייתכן שהקובץ הפתוח כרגע עדיין לא הצורה המלאה. די נפוץ שהגדרות רשומה, דגלים משותפים, host variable ל-SQL, וממשקים חיצוניים דחוסים בתוך ה-copybook. כשקשה לקרוא, מהיר יותר לבדוק אם אפשר לראות את המקור אחרי הרחבה, או את ה-compiler listing, ול-IBM Enterprise COBOL יש גם אפשרות בשם MDECK שמוציאה את מקור הקלט אחרי עיבוד הספרייה.