Back to Contents


The Channel class

Summary:

See also: Built-in classes


Basics

Purpose:

The Channel class provides basic read/write functionality to access files or to communicate with sub-processes.

Syntax:

base.Channel

Methods:

Class Methods
Name Description
create() Creates a new channel object.
Object Methods
Name Description
openFile( path STRING, flags STRING ) Opens a channel to a file identified by path, with options.

The parameters flags can be one of:

  • b : Open in binary mode, to avoid CR/LF translation.
  • r : For Read mode: reads from a file (standard input if path is NULL).
  • w : For Write mode: start with an empty file (standard output if the path is NULL). 
  • a : For Append mode: writes at the end of a file (standard output if the path is NULL).
  • u : For Read from standard input and write to standard output (path must be NULL). 
openPipe( scmd STRING, flags STRING ) Opens a channel to a process by executing the command scmd, with options.

The parameters flags can be one of:

  • b : Open in binary mode, to avoid CR/LF translation.
  • r : For Read Only.
  • w : For Write Only.
  • a : For Write Only.
  • u : For Read and Write.
setDelimiter( d STRING ) Sets the field delimiter of the channel. The default is DBDELIMITER, or "|" if DBDELIMITER is not defined. If you pass NULL, no delimiter is used.
read( buffer-list ) RETURNING INTEGER Reads data from the input. Here buffer-list is a variable list with the square-brace notation ([param1,param2,...]).
The function returns TRUE if data could be read. 
write( buffer-list ) Writes data to the output. Here buffer-list is a variable list with the square-brace notation ([param1,param2,...]).
readLine() RETURNING STRING Reads a line of data from the channel and returns the string.
This method must be used if the source stream does not contain lines with field separators.
Returns an empty string if the line is empty.
Returns NULL when the end of stream is found.
writeLine( buffer STRING ) Writes a line of data to the channel.
This method must be used if the target stream must not contain lines with field separators.
close() Closes the channel.

Errors:

One of the following exceptions may occur while using Channels:

Error number Description
-6340 The channel could not be opened for a file.
-6341 The required open mode is not supported for a file channel.
-6342 The channel could not be opened for a pipe.
-6343 The required open mode is not supported for a pipe channel.
-6344 Cannot write to an unopened channel.
-6345 An error occurred while writing to the channel. 
-6346 Cannot read from an unopened channel.

Warning!: As in other BDL instructions, when reading or writing strings, the escape character is the backslash "\".

Usage:

The Channel class is a built-in class that provides basic read/write functionality to access files or to communicate with sub-processes.

First you must declare a base.Channel variable, then create a Channel object, and optionally set the field delimiter:

01 DEFINE ch base.Channel
02 LET ch = base.Channel.create()
03 CALL ch.setDelimiter("^")

You can open a file for reading, writing, or both, by using the openFile() method:

01 CALL ch.openFile( "file.txt", "w" )

When using "w" or "a" modes, the file is created if it does not exist.

With the openPipe() method, you can read from the standard output of a sub-process, write to the standard input, or both.

01 CALL ch.openPipe( "ls", "r" )

When the channel is open, you can read and/or write data from/to the input/output. You must provide a variable list by using the the square brace notation ([param1,param2,...]). The read function returns TRUE if data could be read.

01 DEFINE a,b INTEGER
02 DEFINE c,d CHAR(20)
03 WHILE ch1.read([a,b,c,d])
04   CALL ch2.write([a,b,c,d])
05 END WHILE

You can trap exceptions with the standard WHENEVER ERROR exception handler:

01 WHENEVER ERROR CONTINUE
02 CALL ch.write([num,label])
03 IF STATUS THEN
04    ERROR "An error occurred while reading from channel"
05    CALL ch.close()
06    RETURN -1
07 END IF
08 WHENEVER ERROR STOP

When you have finished with it, close the channel with the close() method:

01 CALL ch.close()

A channel is automatically closed when the last reference to the channel object is deleted.

By default, a CR character represents the end of a channel record. If you want to write a CR as part of the line, the string must hold the backslash and CR as two independent characters. This means, that you need to escape the backslash when you write the string constant in the BDL source file.

For example, the following code:

01 CALL ch.write("aaa\\\nbbb")  -- Holds one backslash + one CR
02 CALL ch.write("ccc\nddd")    -- Holds one CR only

... would generate the following output:

01 aaa\
02 bbb
03 ccc
04 ddd

Where line 01 and 02 hold data for the same line in the meaning of a channel record.

When you read these lines back with a channel, you get the following strings in memory:

Read 1 aaa[bs][cr]bbb
Read 2 ccc
Read 3 ddd

These reads would correspond to the following assignments when using string constants:

01 LET s = "aaa\\\nbbb"
02 LET s = "ccc"
03 LET s = "ddd"

If the stream does not contain lines with fields (and field separators), you should use the readLine() and writeLine() methods. These methods read/write a complete line from/to the channel, by ignoring the delimiter defined by setDelimiter():

01 CALL ch.writeLine("this is a complete line")
02 LET buff = ch.readLine()

Line terminators on Windows platforms

On Windows platforms, DOS formatted text files use CR/LF as line terminators. In this section we will describe how you can manage such type of files with the Channel class.

By default, on both Windows and Unix platforms, when reading records from a DOS file with the channel class, the CR/LF line terminator is removed. When writing a record to a file, on Windows, the lines are terminated with CR/LF in the file, while on UNIX, the lines are terminated with LF only.

If you want to avoid the automatic translation of CR/LF, you can use b option of the openFile() and openPipe() methods. You typically combine the b option with r or w, according to the read or write operations you want to do:

01 CALL ch.openFile( "mytext.txt", "rb" )

With the b option, when reading lines, on both Windows and UNIX platforms, only LF is removed from CR/LF line terminators. This means, that CR will be copied as a character part of the last field. On the opposite, when writing lines on Windows, LF characters will not be converted to CR/LF. On UNIX, writing lines with or without binary mode does not matter.


Examples

Example 1:

This program reads data from the file "file.txt" file that contains 2 columns separated by a | (pipe) character, and re-writes these data at the end of the "fileout.txt" file, separated by "%"

01 MAIN
02     DEFINE buff1, buff2 STRING
03     DEFINE ch_in, ch_out base.Channel
04     LET ch_in = base.Channel.create()
05     CALL ch_in.setDelimiter("|")
06     LET ch_out = base.Channel.create()
07     CALL ch_out.setDelimiter("%")
08     CALL ch_in.openFile("file.txt","r")
09     CALL ch_out.openFile("fileout.txt","w")
10     WHILE ch_in.read([buff1,buff2])
11         CALL ch_out.write([buff1,buff2])
12     END WHILE
13     CALL ch_in.close()
14     CALL ch_out.close()
15 END MAIN

Example 2:

This program executes the "ls" command and displays the filenames and extensions separately:

01 MAIN
02     DEFINE fn CHAR(40)
03     DEFINE ex CHAR(10)
04     DEFINE ch base.Channel
05     LET ch = base.Channel.create()
06     CALL ch.setDelimiter(".")
07     CALL ch.openPipe("ls -l","r")
08     WHILE ch.read([fn,ex])
09         DISPLAY fn, "   ", ex
10     END WHILE
11     CALL ch.close()
12 END MAIN