Node.js - Group home

Read and write EBCDIC data by using Node.js encoding support

  

Overview

Working on z/OS systems can sometimes be tricky as z/OS natively supports both EBCDIC and ASCII encodings, among others. Furthermore, z/OS files have an additional attribute, known as a file tag, which needs to be considered when performing file I/O.

As of v14.17.6, IBM SDK for Node.js - z/OS introduced a new "cp1047" encoding option to support the EBCDIC 1047 encoding which allows users to work with EBCDIC files without the need for third party modules or using external conversion tools outside of Node.js.
For Node.js releases older than v14.17.6, check out the mvsutils npm for EBCDIC encoding support. A more detailed blog post demonstrating its usage can be found here.

Getting started

A minimum version of v14.17.6 SDK for Node.js - z/OS is required for the "cp1047" encoding support. Download SDK for Node.js - z/OS for v14.17.6 or later and follow the IBM Documentation installation instructions to install Node.js on z/OS.

Reading files

Let's say you want to read a file named unknown-tag-ebcdic-file. As its name suggests, you do not know whether the file was tagged correctly or even tagged at all, but one thing you know is that its data is in EBCDIC encoding.

Instead of manually tagging unknown-tag-ebcdic-file, which can be time consuming, error-prone and troublesome, especially if there are several files that you want to work on and they are mixed with files in other encodings, you can simply use the readFile function from the fs library in Node.js with the "cp1047" encoding option to read the file:

const fs = require('fs');

fs.readFile('unknown-tag-ebcdic-file', 'cp1047', (err, data) => {

  if (err) {

    console.error('oops, something went wrong...');

    return;

  }

  console.log(`Finished reading data: ${data}`);

});

The encoding option indicates that
readFile would return the data in EBCDIC encoding. Running the above code should display:

Finished reading data: some ebcdic data...


Now
you can read files that are incorrectly tagged or untagged without the need to go outside of Node.js, but what about writing EBCDIC data?

Writing files

After processing the data that you read from unknown-tag-ebcdic-file, you want to save the new data into a new file processed-ebcdic-file while keeping it in EBCDIC.

You can use writeFile to achieve this:

const fs = require('fs');

fs.readFile('unknown-tag-ebcdic-file', 'cp1047', (err, data) => {

  if (err) {

    console.error('oops, something went wrong...');

    return;

  }

  console.log(`Finished reading data: ${data}`);




  const newData = processData(data);

  console.log(`Finished processing data: ${newData}`);

 

  fs.writeFile('processed-ebcdic-file', newData, 'cp1047', (err) => {

    console.log('Finished writing.');

  });

});


Running the above code produces
:

Finished reading data: some ebcdic data...

Finished processing data: processed ebcdic data...

Finished Writing.


You can now verify the tagging of processed-ebcdic-file with the ls -T option:

$ ls -T processed-ebcdic-file

t IBM-1047    T=on  processed-ebcdic-file

$ cat processed-ebcdic-file

processed ebcdic data...


IBM-1047
indicates that the file is tagged as EBCDIC, and as the
the cat command shows, the data is shown correctly which means the tag matches the encoding of the data.

*Notice that readFile and writeFile are asynchronous functions. If you want to read or write unknown-tag-ebcdic-file synchronously, you can use readFileSync or writeFileSync instead.
readFile and writeFile are good for basic use cases, but what about reading or writing a large file, or having control of the IO stream? You can use the stream class from Node.js for that.

Stream

After you are done with unknown-tag-ebcdic-file, you want to process another EBCDIC file unknown-tag-ebcdic-large-file, which again you don't know much about its tagging information. You will want to use stream this time since the file is much larger in size.


Read

To read
unknown-tag-ebcdic-large-file, you can use fs.createReadStream:


const fs = require('fs');

const stream = fs.createReadStream('unknown-tag-ebcdic-large-file', 'cp1047');

stream.data = '';

stream.on('data', (chunk) => {

  stream.data += chunk;

});

stream.on('end', () => {

  console.log(`Finished reading data: ${stream.data}`);

});


Node.js s
treams allows unknown-tag-ebcdic-large-file to be read in chunks, and the above code simply concatenates the data via the on('data') event. The produced output of code above would be:


Finish reading data: some more and more and more ebcdic data...



Write

Similarly,
after processing the data from unknown-tag-ebcdic-large-file, you want to write the new data to a new file processed-ebcdic-large-file in EBCDIC:


const stream = fs.createReadStream('unknown-tag-ebcdic-large-file', 'cp1047');

stream.data = '';

stream.on('data', (chunk) => {

  stream.data += chunk;

});

stream.on('end', () => {

  console.log(`Finished reading data: ${stream.data}`);




  const newData = processData(stream.data);

  console.log(`Finished processing data: ${newData}`);




  const stream2 = fs.createWriteStream('processed-ebcdic-large-file', 'cp1047');

  stream2.on('finish', () => {

    console.log('Finished writing.');

  });

  stream2.write(newData);

  stream2.end();

});


The output would be:

Finished reading data: some more and more and more ebcdic data...

Finished processing data: processed more and more and more ebcdic data...

Finished Writing.


Verifying processed-ebcdic-large-file:


$ ls -T processed-ebcdic-large-file

t IBM-1047    T=on  processed-ebcdic-large-file

$ cat processed-ebcdic-large-file

processed more and more and more ebcdic data...


Now you are able to read and write large EBCDIC files efficiently using stream, but before we wrap up, let's explore the pipe function in stream and see how you can streamline the process of unknown-tag-ebcdic-large-file even more.


Pipe

If your processing function is a Transform stream, you can further simplify the above code:
 

fs.createReadStream('unknown-tag-ebcdic-large-file', 'cp1047')

  .pipe(processDataTS)  // processDataTS is a Transform stream that does the processing

  .pipe(fs.createWriteStream('processed-ebcdic-large-file', 'cp1047'));


You have reduced the previous code into 3 lines of code! This
looks much simpler and cleaner thanks to pipe!


*Notice that if "cp1047" is set as the encoding, it would ignore the existing tag or options like __UNTAGGED_READ_MODE. If you have any feedback or suggestions regarding the "cp1047" encoding support, feel free to leave a comment below, and we will respond ASAP.

You are now able to read and write EBCDIC data without the need to manually handle file tagging in z/OS UNIX or to use any third party tools, and just by using Node.js itself! Great, but what's next? Check below for more interesting stuff in Node.js!


Additional Resources


Tagging files with Node.js on z/OS using mvsutils - https://community.ibm.com/community/user/ibmz-and-linuxone/blogs/wayne-zhang1/2021/08/05/tagging-files-with-nodejs-on-zos-using-mvsutils

Language interoperability with Node.js on z/OS: Part 1 - REXX - https://community.ibm.com/community/user/ibmz-and-linuxone/blogs/igor-todorovski/2021/08/23/language-interop-nodejs-part-1

Node.js
IBM SDK for Node.js - z/OS 14.0 – Updated to community release v14.17.6! - https://community.ibm.com/community/user/ibmz-and-linuxone/blogs/dhrumil-rana/2021/11/12/ibm-sdk-for-nodejs-zos-140-updated-to-community-re