COBOL Module 9 – EBCDIC, record layouts, Unicode and JSON
UTF-8, national data and names in Indian scripts
See why byte-based PIC X fields break UTF-8 names in Devanagari and other Indian scripts, how to size and check them, and what national data offers.
What you will learn
- Explain why byte-based PIC X fields break UTF-8 text such as names in Devanagari
- Size fields for names in Indian scripts and check that a field holds valid UTF-8
- Write a routine that cuts a UTF-8 name to a byte limit without splitting a character
- Compare national (UTF-16) and UTF-8 support in IBM Enterprise COBOL 6.5 and GnuCOBOL 3.2
Before you start
What you’ll learn · Before you start
What you will learn
- Explain why byte-based PIC X fields break UTF-8 text such as names in Devanagari
- Size fields for names in Indian scripts and check that a field holds valid UTF-8
- Write a routine that cuts a UTF-8 name to a byte limit without splitting a character
- Compare national (UTF-16) and UTF-8 support in IBM Enterprise COBOL 6.5 and GnuCOBOL 3.2
Before you start
On this page
The name अनीता देशमुख is 12 characters and 34 bytes in UTF-8, so a PIC X(30) field that looks roomy cuts it, and a PIC X(10) field cuts the letter त in half. COBOL’s PIC X counts bytes, and names in Indian scripts take about three bytes a character.
This lesson measures names in six scripts, shows what MOVE, UPPER-CASE and REVERSE do to UTF-8 text in GnuCOBOL, and names what national data and IBM’s UTF-8 items count instead.
A PIC X field counts bytes, and UTF-8 needs up to four for a character
In UTF-8: A Unicode encoding that stores each code point in one to four bytes: one for ASCII characters, three for most characters of the Indian scripts. A byte from X'80' to X'BF' only continues a character. a character takes one to four bytes; IBM’s Programming Guide says its encoding “is always between one and four bytes”. The Latin letters of ASCII take one, and a Devanagari letter takes three. A PIC X field, and every statement that works on it, counts bytes. names.cob puts the name in byte-based fields and calls three text functions on it, and valuecut.cob gives the name, as a VALUE, to a field of 30 bytes:
IDENTIFICATION DIVISION.
PROGRAM-ID. NAMES.
* A name in Devanagari in byte-based PIC X fields.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 NAME-40 PIC X(40).
01 NAME-10 PIC X(10).
01 WS-BYTES PIC 99.
01 NAME-REV PIC X(10).
PROCEDURE DIVISION.
MOVE "अनीता देशमुख" TO NAME-40
MOVE FUNCTION LENGTH (FUNCTION TRIM (NAME-40)) TO WS-BYTES
DISPLAY "bytes used: " WS-BYTES
DISPLAY "as written: [" FUNCTION TRIM (NAME-40) "]"
MOVE NAME-40 TO NAME-10
DISPLAY "into X(10): [" NAME-10 "]"
DISPLAY "its bytes: " FUNCTION HEX-OF (NAME-10)
DISPLAY "UPPER-CASE: ["
FUNCTION UPPER-CASE (FUNCTION TRIM (NAME-40)) "]"
MOVE FUNCTION REVERSE (NAME-10) TO NAME-REV
DISPLAY "REVERSE: " FUNCTION HEX-OF (NAME-REV)
STOP RUN. from session import files, run
# A name in Devanagari in byte-based PIC X fields, then as the VALUE of a field four bytes too short.
# -A passes an option to the C compiler: it quiets clang's warning about the UTF-8 bytes of the literals.
files("names.cob", "valuecut.cob")
run("""
cobc -x -A -Wno-invalid-source-encoding names.cob && ./names
cobc -x -A -Wno-invalid-source-encoding valuecut.cob && ./valuecut | xxd
""") Output
$ cobc -x -A -Wno-invalid-source-encoding names.cob && ./names
bytes used: 34
as written: [अनीता देशमुख]
into X(10): [अनी�]
its bytes: E0A485E0A4A8E0A580E0
UPPER-CASE: [अनीता देशमुख]
REVERSE: E080A5E0A8A4E085A4E0
$ cobc -x -A -Wno-invalid-source-encoding valuecut.cob && ./valuecut | xxd
valuecut.cob:7: warning: value size exceeds data size [-Wothers]
5 | WORKING-STORAGE SECTION.
6 | 01 NAME-30 PIC X(30)
7 > VALUE "अनीता देशमुख".
8 | PROCEDURE DIVISION.
9 | DISPLAY NAME-30
valuecut.cob:7: note: value size is 34 [-Wothers]
00000000: e0a4 85e0 a4a8 e0a5 80e0 a4a4 e0a4 be20 ...............
00000010: e0a4 a6e0 a587 e0a4 b6e0 a4ae e0a5 0a ...............
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 names_session.py
The name took 34 bytes. The MOVE into PIC X(10) stopped after byte 10, which is the first of the three bytes of त, so the field ends with a lone E0 that DISPLAY shows as a replacement mark. UPPER-CASE left the name unchanged, since Devanagari has no capitals, and REVERSE reversed bytes, not characters, so its result is not UTF-8 at all. The VALUE clause compiled with only a warning, “value size exceeds data size”, and a note that the value is 34 bytes, and its xxd listing ends with the bytes e0 a5 before DISPLAY’s newline: the vowel sign ु was cut in two at byte 30. Step through the MOVE:
The drawing shows the whole scene; its 6 steps are listed below.
Every step as text
- Twelve characters, 34 bytes. अनीता देशमुख has 12 characters. In UTF-8 each Devanagari character takes three bytes and the space one, so the name is 34 bytes, the number names.cob printed.
- The first character. अ is E0 A4 85. E0 starts a character of three bytes, and A4 and 85 only continue it.
- A vowel sign is a character too. न is bytes 4 to 6, and the vowel sign ी, which a reader sees as part of नी, is bytes 7 to 9. Both are characters of their own in Unicode.
- The tenth byte. The fourth character, त, starts at byte 10. A PIC X(10) field has room for its first byte, E0, and none for the other two.
- The MOVE. MOVE fills NAME-10 byte by byte and stops after byte 10. Nothing in PIC X knows that byte 10 began a character.
- A broken last character. DISPLAY shows अनी and then a replacement mark, because the lone E0 starts a character that never ends. A file or a database would receive the broken byte as it is.
A name field sized in characters
Wrong: PIC X(30) for “up to 30 characters”, which holds 10 characters of Devanagari. Right: size a PIC X field in bytes, three for each character of the Indian scripts, and never let a MOVE or a VALUE cut the text where it likes.
Names in Indian scripts take three bytes a character, and national data counts characters
name_sizes.py measures seven invented names in six scripts, in code point: The number Unicode gives a character, such as U+0905 for the Devanagari letter अ. What a reader sees as one letter may be several code points, such as a consonant with a vowel sign., in UTF-8 bytes, which a PIC X field holds, and in UTF-16: A Unicode encoding that stores most characters, those of the Indian scripts included, in two bytes. IBM's Enterprise COBOL keeps national data in UTF-16, CCSID 1200. bytes, which national data holds:
"""How big a name is in characters (Unicode code points), in UTF-8 bytes, which is what a PIC X field counts, and in
UTF-16 bytes, which is what national data holds. The names are invented; add your own to NAMES and run it again."""
NAMES = [
"Anita Deshmukh",
"José Fernandes",
"अनीता देशमुख",
"கவிதா ராமன்",
"সুমিত ঘোষ",
"ਹਰਪ੍ਰੀਤ ਕੌਰ",
"లక్ష్మి రెడ్డి",
]
print(f"{'code points':>12} {'UTF-8 bytes':>12} {'UTF-16 bytes':>13} name")
for name in NAMES:
print(f"{len(name):12} {len(name.encode('utf-8')):12} {len(name.encode('utf-16-be')):13} {name}")
widest = max(len(name.encode("utf-8")) for name in NAMES)
print(f"The longest name here needs PIC X({widest}) in UTF-8.") Output
code points UTF-8 bytes UTF-16 bytes name
14 14 28 Anita Deshmukh
14 15 28 José Fernandes
12 34 24 अनीता देशमुख
11 31 22 கவிதா ராமன்
9 25 18 সুমিত ঘোষ
11 31 22 ਹਰਪ੍ਰੀਤ ਕੌਰ
14 40 28 లక్ష్మి రెడ్డి
The longest name here needs PIC X(40) in UTF-8.
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 name_sizes.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
Names in Indian scripts took about three UTF-8 bytes a code point: the Telugu name has 14 code points and needs 40 bytes, while the Latin name of 14 takes 14, and José takes 15 for its é. In UTF-16 every one of these names takes two bytes a code point, so names in Indian scripts are smaller in UTF-16 than in UTF-8, and Latin names the other way round. A vowel sign or a virama is a code point of its own, so a name of five visible letters may be more code points than that.
Add a name of your own to NAMES and run the program again to see how many bytes its field needs.
COBOL has a second way to hold text. national data: COBOL data items with USAGE NATIONAL and PICTURE N, which Enterprise COBOL holds as UTF-16, so that a field counts characters rather than the bytes of UTF-8., USAGE NATIONAL with PICTURE N, is UTF-16 in Enterprise COBOL: “Define national data items with the USAGE NATIONAL clause to hold national (UTF-16) character strings.” Enterprise COBOL 6.5 also has UTF-8 data items, PICTURE U, which count code points rather than bytes. In GnuCOBOL 3.2, NEWS lists only “parsing support” for USAGE UTF-8, so a program there keeps UTF-8 text in PIC X and must do the counting itself.
| Field | Holds | Counts | Bytes for the Telugu name |
|---|---|---|---|
| PIC X(n) | UTF-8 bytes | bytes | 40 |
| PIC N(n) NATIONAL | UTF-16 | characters | 28 |
Checkpoint
Check: PIC X counts bytes, not characters · UTF-8 and UTF-16 sizes of names
Practise: bytes, characters and fields
These questions use the name, the runs and the table of sizes.
Practice that adapts to you
Practise this lesson
4 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Sizing and validating UTF-8 fields: a safe cut, a validity check and a conjunct kept whole Pro: opens with a Pro, Premium or Ultimate pass
A program that must fit names into fixed fields needs three routines: one that cuts without splitting a character, one that proves a field holds valid UTF-8, and a rule that keeps a written letter whole. This part builds the first two in COBOL and shows the third with names that need it.
In this part:
- Cutting a name to a byte limit without splitting a character
- Cut a UTF-8 name to a byte limit without splitting a character
- Results of the sample tests
- Checking that a field holds valid UTF-8, as UVALID does
- A whole code point can still break a conjunct
1 diagram · 5 runnable examples (COB, Python) · 1 exercise with sample tests · about 9 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
National and UTF-8 data in Enterprise COBOL 6.5 and GnuCOBOL 3.2, compared Pro: opens with a Pro, Premium or Ultimate pass
The same name can live in four kinds of field on z/OS, and in only one of them in GnuCOBOL 3.2. This part sets out what Enterprise COBOL 6.5 offers, compiles the same program with GnuCOBOL, and turns the comparison into a choice.
In this part:
- What Enterprise COBOL 6.5 offers for Unicode data
- What GnuCOBOL 3.2 does with the same program
- One encoding at every boundary, tested with names in several scripts
3 runnable examples (COB, Python) · about 6 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Interview questions
A warm-up with its answer, then three questions about names that did not fit:
-
Fresher to mid · Warm-up
Why can a PIC X(30) field not hold a 30-character name in Devanagari?
Because PIC X counts bytes, and UTF-8 stores a Devanagari character in three bytes. PIC X(30) holds 30 bytes, so at most 10 Devanagari characters: the 12 characters of अनीता देशमुख already take 34 bytes. A MOVE or a VALUE into a field that is too short cuts the bytes where the field ends, which can split a character and leave a lone lead byte such as E0, and byte-based functions such as REVERSE and LENGTH work on bytes too. Size UTF-8 fields in bytes, about three for each character of the Indian scripts. On Enterprise COBOL, national data, which is UTF-16, takes two bytes for each of these characters, and UTF-8 items with PICTURE U count code points; GnuCOBOL 3.2 only parses USAGE UTF-8, so there the text stays in PIC X.
-
Mid to senior
Customer names in Hindi and Tamil arrive from a web form in UTF-8. After a nightly COBOL job copies them into PIC X(40) fields, the next system rejects some records for invalid characters. What happened, and how do you fix and prove it?
-
Fresher to mid
A new interface must carry customer names of up to 40 characters in any Indian script, or in Latin letters. How many bytes do you give the field in a GnuCOBOL program that keeps UTF-8 in PIC X, and what else goes into the interface description?
-
Senior to staff
A bank plans to store names in Indian scripts in a z/OS COBOL application whose unit tests run with GnuCOBOL on developers' computers. Would you use national data, UTF-8 items or PIC X fields, and why?
Organisations named on this page
IBM’s Enterprise COBOL pages, RFC 3629 and GnuCOBOL’s NEWS are the sources of the Unicode rules on this page, and every name in it is invented. MySmartCoPilot cites IBM, the GnuCOBOL project and the Python Software Foundation and is part of none of them.
Flashcards · 6 cards
Recall it, then check
Say the answer to yourself before you look: recalling it is what makes it stick.
-
How many bytes does a character take in UTF-8?
One to four: an ASCII letter takes one, é takes two and a Devanagari letter takes three.
-
Why does PIC X(30) not hold a name of 30 Devanagari letters?
PIC X counts bytes, and each Devanagari letter needs three, so 30 bytes hold 10 of them. The lesson's name of 12 characters needed 34 bytes.
-
What can a MOVE of a UTF-8 name into a short field do to it?
It stops at the byte limit wherever that falls, so it can split a character, leaving a lone byte that DISPLAY shows as a replacement mark.
-
What do UPPER-CASE and REVERSE do to a Devanagari name in GnuCOBOL?
UPPER-CASE leaves it as it is, since the script has no capitals, and REVERSE reverses its bytes, not its characters, so the result is not valid UTF-8.
-
How many bytes a code point do names in Indian scripts take in UTF-8 and in UTF-16?
About three in UTF-8 and two in UTF-16, so national data holds them in fewer bytes; Latin names are the other way round.
-
What is national data, and what does GnuCOBOL 3.2 offer instead?
USAGE NATIONAL with PICTURE N holds UTF-16 in Enterprise COBOL, and its PICTURE U items hold UTF-8 counted in code points. GnuCOBOL 3.2 only parses USAGE UTF-8, so a program there keeps UTF-8 in PIC X.
Key takeaways
- PIC X fields and the statements on them count bytes, so a MOVE, a VALUE or REVERSE can cut or scramble UTF-8 text, and a Devanagari name of 12 characters needs 34 bytes.
- Names in Indian scripts take about three UTF-8 bytes a code point, and two in UTF-16, so national data holds them in fewer bytes than UTF-8, and Latin names the other way round.
- Enterprise COBOL 6.5 has national data in UTF-16 and UTF-8 items that count code points, while GnuCOBOL 3.2 only parses USAGE UTF-8, so portable programs keep UTF-8 in PIC X and do the counting themselves.
Terms in this lesson
- code point
- The number Unicode gives a character, such as U+0905 for the Devanagari letter अ. What a reader sees as one letter may be several code points, such as a consonant with a vowel sign.
- conjunct
- Consonants joined by a virama, such as क्ष in Devanagari, which a reader sees as one shape but which Unicode stores as three code points.
- continuation byte
- A UTF-8 byte from X'80' to X'BF', which never starts a character but continues the one before it. A cut placed just before one splits a character.
- national data
- COBOL data items with USAGE NATIONAL and PICTURE N, which Enterprise COBOL holds as UTF-16, so that a field counts characters rather than the bytes of UTF-8.
- UTF-16
- A Unicode encoding that stores most characters, those of the Indian scripts included, in two bytes. IBM's Enterprise COBOL keeps national data in UTF-16, CCSID 1200.
- UTF-8
- A Unicode encoding that stores each code point in one to four bytes: one for ASCII characters, three for most characters of the Indian scripts. A byte from X'80' to X'BF' only continues a character.
References
- Enterprise COBOL for z/OS 6.5 Programming Guide: Using national data (Unicode) in COBOL (IBM)
- Enterprise COBOL for z/OS 6.5 Programming Guide: Defining UTF-8 data items (IBM)
- Enterprise COBOL for z/OS 6.5: CODEPAGE compiler option (IBM)
- Enterprise COBOL for z/OS 6.5: NATIONAL-OF (IBM)
- Enterprise COBOL for z/OS 6.5: ULENGTH (IBM)
- Enterprise COBOL for z/OS 6.5: UVALID (IBM)
- RFC 3629: UTF-8, a transformation format of ISO 10646 (IETF)
- GnuCOBOL 3.2 NEWS (GnuCOBOL project (GNU))
- Python 3.14 documentation: unicodedata (Python Software Foundation)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress