Using Unformatted Input/Output
Unformatted input/output involves the direct transfer of data between a file and memory without conversion to and from a character representation. Unformatted input/output is used when efficiency is important and portability is not an issue. It is faster and requires less space than formatted input/output. IDL provides three procedures for performing unformatted input/output:
READU
Reads unformatted data from the specified file unit.
WRITEU
Writes unformatted data to the specified file unit.
ASSOC
Maps an array structure to a logical file unit, providing efficient and convenient direct access to data.
This section discusses READU and WRITEU, while ASSOC is discussed in Associated Input/Output. The READU and WRITEU procedures provide IDL's basic unformatted input/output capabilities. They have the form:
where
Unit — The logical file unit with which the input/output operation will be performed.
Vari — One or more IDL variables (or expressions in the case of output).
The WRITEU procedure writes the contents of its arguments directly to the file, and READU reads exactly the number of bytes required by the size of its arguments. Both cases directly transfer binary data with no interpretation or formatting.
Unformatted Input/Output of String Variables
Strings are the only basic IDL data type that do not have a fixed size. A string variable has a dynamic length that is dependent only on the length of the string currently assigned to it. Thus, although it is always possible to know the length of the other types, string variables are a special case. IDL uses the following rules to determine the number of characters to transfer:
Input
Input enough bytes to fill the original length of the string. The length of the resulting string is truncated if the string contains a null byte.
Output
Output the number of bytes contained in the string. This number is the same number returned by the STRLEN function and does not include a terminating null byte.
Note that these rules imply that when reading into a string variable from a file, you must know the length of the original string so as to be able to initialize the destination string to the correct length. For example, the following IDL statements produce the following output, because the receiving variable A was not long enough.
;Open a file. OPENW, 1, 'temp.tmp' ;Write an 11-character string. WRITEU, 1, 'Hello World' ;Rewind the file. POINT_LUN, 1, 0 ;Prepare a nine-character string. A = ' ' ;Read back in the string. READU, 1, A ;Show what was input. PRINT, A CLOSE, 1
produce the following, because the receiving variable A was not long enough:
The only solution to this problem is to know the length of the string being input. The following IDL statements demonstrate a useful "trick" for initializing strings to a known length:
;Open a file. OPENW, 1, 'temp.tmp' ;Write an 11-character string. WRITEU, 1, 'Hello World' ;Rewind the file. POINT_LUN, 1, 0 ;Create a string of the desired length initialized with blanks. ;REPLICATE creates a byte array of 11 elements, each element ;initialized to 32, which is the ASCII code for a blank. Passing ;this byte array to STRING converts it to a scalar string ;containing 11 blanks. A = STRING(REPLICATE(32B,11)) ;Read in the string. READU, 1, A ;Show what was input. PRINT, A CLOSE, 1
This example takes advantage of the special way in which the BYTE and STRING functions convert between byte arrays and strings. See the description of the BYTE and STRING functions for additional details.
Example: Reading C-Generated Unformatted Data with IDL
The following C program produces a file containing employee records. Each record stores the first name of each employee, the number of years he has been employed, and his salary history for the last 12 months.
#include <stdio.h> main() { static struct rec { char name[32]; /* Employee's name */ int years; /* # of years with company */ float salary[12]; /* Salary for last 12 months */ } employees[] = { { {'B','u','l','l','w','i','n','k','l','e'}, 10, {1000.0, 9000.97, 1100.0, 0.0, 0.0, 2000.0, 5000.0, 3000.0, 1000.12, 3500.0, 6000.0, 900.0} },{ {'B','o','r','r','i','s'}, 11, {400.0, 500.0, 1300.10, 350.0, 745.0, 3000.0, 200.0, 100.0, 100.0, 50.0, 60.0, 0.25} }, { {'N','a','t','a','s','h','a'}, 10, {950.0, 1050.0, 1350.0, 410.0, 797.0, 200.36, 2600.0, 2000.0, 1500.0, 2000.0, 1000.0, 400.0} }, { {'R','o','c','k','y'}, 11, {1000.0, 9000.0, 1100.0, 0.0, 0.0, 2000.37, 5000.0, 3000.0, 1000.01, 3500.0, 6000.0, 900.12}} }; FILE *outfile; outfile = fopen("data.dat", "w"); (void) fwrite(employees, sizeof(employees), 1, outfile); (void) fclose(outfile); }
Running this program creates the file data.dat containing the employee records. The following IDL statements can be used to read and print this file:
;Create a string with 32 characters so that the proper number of ;characters will be input from the file. REPLICATE is used to ;create a byte array of 32 elements, each containing the ASCII code ;for a space (32). STRING turns this byte array into a string ;containing 32 blanks. STR32 = STRING(REPLICATE(32B, 32)) ;Create an array of four employee records to receive the input ;data. A = REPLICATE({EMPLOYEES, NAME:STR32, YEARS:0L, $ SALARY:FLTARR(12)}, 4) ;Open the file for input. OPENR, 1, 'data.dat' ;Read the data. READU, 1, A CLOSE, 1 ;Show the results. PRINT, A
Executing these IDL statements produces the following output:
{ Bullwinkle 10
1000.00 9000.97 1100.00 0.00000 0.00000 2000.00
5000.00 3000.00 1000.12 3500.00 6000.00 900.000
}{Borris 11
400.000 500.000 1300.10 350.000 745.000 3000.00
200.000 100.000 100.000 50.0000 60.0000 0.250000
}{ Natasha 10
950.000 1050.00 1350.00 410.000 797.000 200.360
2600.00 2000.00 1500.00 2000.00 1000.00 400.000
}{ Rocky 11
1000.00 9000.00 1100.00 0.00000 0.00000 2000.37
5000.00 3000.00 1000.01 3500.00 6000.00 900.120
}
Example: Reading IDL-Generated Unformatted Data with C
The following IDL program creates an unformatted data file containing a 5 x 5 array of floating-point values:
;Open a file for output. OPENW, 1, 'data.dat' ;Write 5x5 array with each element set to its 1-dimensional index. WRITEU, 1, FINDGEN(5, 5) CLOSE, 1
This file can be read and printed by the following C program:
#include <stdio.h> main() { float data[5][5]; FILE *infile; int i, j; infile = fopen("data.dat", "r"); (void) fread(data, sizeof(data), 1, infile); (void) fclose(infile); for (i = 0; i < 5; i++) { for (j = 0; j < 5; j++) { printf("%8.1f", data[i][j]); printf("\n"); } } }
Running this program gives the following output:
0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0 15.0 16.0 17.0 18.0 19.0 20.0 21.0 22.0 23.0 24.0
Example: Reading a Sun Rasterfile from IDL
Sun computers use rasterfiles to store scanned images. This example shows how to read such an image and display it using IDL. In the interest of keeping the example brief, a number of simplifications are made, no error checking is performed, and only 8-bit deep rasterfiles are handled. See the READ_SRF procedure (the file read_srf.pro in the lib subdirectory of the IDL distribution) for a complete example. The format used for rasterfiles is documented in the C header file /usr/include/rasterfile.h. That file provides the following information:
Each file starts with a fixed header that describes the image. In C, this header is defined as follows:
struct rasterfile{
int ras_magic; /* magic number */
int ras_width; /* width (pixels) of image */
int ras_height; /* height (pixels) of image */
int ras_depth; /* depth (1, 8, or 24 bits) */
int ras_length; /* length (bytes) of image */
int ras_type; /* type of file */
int ras_maptype; /* type of colormap */
int ras_maplength; /* length(bytes) of colormap */ };
The color map, if any, follows directly after the header information. The image data follows directly after the color map.
The following IDL statements read an 8-bit deep image from the file ras.dat:
;Define IDL structure that matches the Sun-defined rasterfile ;structure. A C int variable on a Sun corresponds to an IDL LONG ;int. h = {rasterfile, magic:0L, width:0L, height:0L, depth: 0L,$ length:0L, type:0L, maptype:0L, maplength:0L} ;Open the file, allocating a file unit at the same time. OPENR, unit, file, /GET_LUN ;Read the header information. READU, unit, h ;Is there a color map? IF ((h.maptype EQ 1) AND (h.maplength NE 0) ) THEN BEGIN ;Calculate length of each vector. maplen = h.maplength/3 ;Create three byte vectors to hold the color map. r=(g=(b=BYTARR(maplen, /NOZERO))) ;Read the color map. READU, unit, r, g, b ENDIF ;Create a byte array to hold image. image = BYTARR(h.width, h.height, /NOZERO) ;Read the image. READU, unit, image ;Free the previously-allocated Logical Unit Number and close the ;file. FREE_LUN, unit