The need to convert between PC ASCII and EBCDIC is pretty widespread, and I'm always getting emails asking for a way to do this type of conversion. Of course, my first suggestion is to use the RPG xTools ToAscii() and ToEbcdic() subprocedures. They use a higher-speed and less-restrictive method to do the conversion than the alternative method I'll present here.
Of course, not everyone has RPG xTools (I hope most of you do something about that this coming year), so I decided to provide a solution I've had since back in the days of System/38. QDCXLATE is an API that has been around for about 20 years. IBM changed the name to QTBXLATE, but nobody seemed to pick up on that, so IBM now supports calling it by either name.
Unlike other methods, QDCXLATE uses a table stored in QUSRSYS, QSYS, or a user-specified library name.
QDCXLATE has an unconventional parameter list. Since it was created back when APIs were not really part of OS/400, the developers didn't really have a set of standards to follow. These early pre-APIs include QCMDEXC, QCMDCHK, QCLSCAN, QDCXLATE, and a few others.
To make this function work effectively and make it easy to call, I've created a simple wrapper procedure for it. This procedure allows you to pass in the data to be converted, along with the length of the data. Optionally, you may override the translation table being used to do the translation. If you don't specify a translation table, the default is to convert the input data to ASCII using the QASCII table in QSYS.
The procedure name is CVTDATA (convert data), and it accepts four parameters:
- szData—The input data. Specify any character variable up to 32K in length. The data in this field will be converted in place. This means the data in the field itself will be translated.
- nLen—The length of the input data specified on parameter 1. Specify the length of the data or the length of the field. Remember, if you want trailing blanks to be converted, specify the length of the field. If you do not want the trailing blanks, specify the length of the data by wrapping %trimR(var) in a %LEN() function as follows: %len(%trimR(myData))
- szTable (optional)—The name of the translation table to be used to convert the data. By default, QASCII in QSYS is used. While any translation table may be specified, the two primary tables that are specified for this parameter are QASCII (to convert to ASCII) and QEBCDIC (to convert to EBCDIC).
- szTableLib (optional)—The library name of the translation table specified on parameter 3. If no library name is specified, QUSRSYS is searched, and then QSYS is searched, and then the library list is searched.
To call this procedure, you need to /INCLUDE or /COPY the prototype for the CVTDATA procedure. I've included the prototype in the source code and highlighted the starting and ending prototype statements.
The return value is 0 if everything goes fine, and -1 if an error is detected during the translation.
Calling CVTDATA
To call the CVTDATA procedure, you need to pass in the data to be converted and the length of the data. Nothing else is required. The input data will be converted to ASCII. Here is an example:
In this example, the data in the field MYDATA is convert to ASCII. Note that since I'm passing in %LEN(MYDATA), the entire field is converted. Had I passed in %LEN(%TRIMR(MYDATA)), then only the data would be converted, not the trailing blanks.
To convert this data back to EBCDIC, you would call the CVTDATA procedure and specify the QEBCDIC value for the third parameter, as follows:
Note that the translation table is quoted.
To more fully illustrate how this procedure works, I wrote a short test program that illustrates its functionality:
/IF DEFINED(*CRTBNDRPG)
H DFTACTGRP(*NO)
/ENDIF
/COPY NEWSLETTER/QCPYSRC,CVTDATA
D myData s 32A Inz('Robert Cozzi, Jr.')
D e_Data s 32A
D a_Data s 32A
C eval *INLR = *ON
** To ASCII
C callp CvtData(myData:%len(myData))
C eval a_Data = myData
** To EBCDIC
C callp CvtData(myData:%len(myData):'QEBCDIC')
C eval e_Data = myData
C return
In this test program, the field MYDATA is convert to ASCII. The resulting ASCII data is then copied to the field named A_DATA. Then MYDATA is converted (from ASCII) back to EBCDIC. The EBCDIC data is copied to the field named E_DATA.
Put this program in debug mode and set break points just after each of the two CALLP statements. Then, inspect the MYDATA field and the content of the A_DATA and E_DATA fields.
The CVTDATA subprocedure source code is illustrated below.
** -------START-PROTOTYPE -- COPY to QCPYSRC/CVTDATA --------
D CvtData PR 10I 0
D szData 32767A OPTIONS(*VARSIZE)
D nLen 10I 0 Const
D szTable 10A Const OPTIONS(*NOPASS)
D szTableLib 10A Const OPTIONS(*NOPASS)
** -------END-PROTOTYPE---------------------------------------
P CvtData B Export
D CvtData PI 10I 0
D szData 32767A OPTIONS(*VARSIZE)
D nLen 10I 0 Const
D szTable 10A Const OPTIONS(*NOPASS)
D szTableLib 10A Const OPTIONS(*NOPASS)
D dftAscii C Const('QASCII')
D dftEBCDIC C Const('QEBCDIC')
D MAX_LENGTH C Const(32767)
D Qdcxlate PR ExtPgm('QDCXLATE')
D dataLen 5P 0 Const
D cvtData 32767A OPTIONS(*VARSIZE)
D xlateTable 10A Const
D xlateTableLib 10A Const OPTIONS(*NOPASS)
D xlateTable DS
D tblName 10A Inz(dftAscii)
D tblLib 10A
** Only 32k can be converted with QDCxLate
C if nLen > MAX_LENGTH
C return -1
C endif
** User-specified xlate table or library name
C if %Parms >= 3
C if szTable <> *BLANKS
C eval tblName = szTable
C if %Parms >= 4
C if szTableLib <> *BLANKS
C eval tblLib = szTableLib
C endif
C endif
C else
C eval xlateTable = dftASCII
C endif
C endif
** Call QDCxLate to do the conversion
C if tblLib <> *BLANKS
C callp(e) qdcXLate(nLen : szData : tblName : tblLib )
C else
C callp(e) qdcXLate(nLen : szData : tblName )
C endif
C if %ERROR
C return -1
C endif
C return 0
P CvtData E
To compile this source code, you can use PDM option 15 to compile the source member, or you can use the CRTRPGMOD command to create a *MODULE:
SRCFILE(RPGLAB/QRPGLESRC) DBGVIEW(*SOURCE)
Then, it's up to you whether you want to leave it as a module or subsequently store it in a *SRVPGM with other subprocedures.
To compile the example program that uses CVTDATA, first copy the prototype to source member QCPYSRC in your library; then, specify that source member on a /COPY statement in the program's source.
The CRTRPGMOD and CRTPGM statements to compile the test program are as follows:
SRCFILE(RPGLAB/QRPGLESRC) DBGVIEW(*SOURCE)
CRTPGM PGM(RPGLAB/TESTCVT) +
MODULE(RPGLAB/TESTCVT RPGLAB/CVTDATA)
My hope is that this helps those who need this kind of functionality. I'd also like to thank all of you for a successful year here at MC RPG Developer (yes I know, most of you didn't know that was the name of this newsletter). In the year ahead, I hope to continue to provide MC RPG Developer for free to all of you. Tell your friends about MC RPG Developer and help us keep it coming to your inbox in 2006.
Bob Cozzi is a programmer/consultant, writer/author, and software developer of the RPG xTools, a popular add-on subprocedure library for RPG IV. His book The Modern RPG Language has been the most widely used RPG programming book for nearly two decades. He, along with others, speaks at and runs the highly-popular RPG World conference for RPG programmers.
LATEST COMMENTS
MC Press Online