Jfiles are component
files for J data. A jfile can
be thought of as a boxed list which is stored on file.
An element of the boxed list is referred to as a component,
and can store a noun of any type, shape or size. File components
are numbered sequentially from 0 upwards.
To access the system (assuming you are using the standard profile):
This populates a locale
jfiles with utility functions, and defines the following verbs in the
jcreate jerase jappend jread jreplace jdup jsize
You create a file using
jappend adds new items to the file. Each item added to the file is stored in a new component. Several items can be added to the file at a time.
jread reads items from file, and
jreplace replaces items on file.
jdup duplicates a file, and
jsize returns its size.
You can refer to a file either by its file name, or by its file handle. The default file extension is .ijf - this is used if no file extension is given.
You cannot delete components once created. If you need to reclaim space no longer required, either replace the components with empties, or else duplicate the file, copying only the components required.
create a file. The right argument is the filename. Any existing file is overwritten. The result is 1 if successful, else 0. For example:
jcreate 'mydata' 1
Note that since the file extension was not specified, this actually creates a file named mydata.ijf.
Read and write file
append to file. The left argument is a boxed list, with each item in the list stored in a new file component. An open noun is treated as a single boxed item. The right argument is the filename. The result is the new component numbers created. For example:
'header' jappend 'mydata' 0 ('rec1';'rec2') jappend 'mydata' 1 2 (< <\1 2 3) jappend 'mydata' 3 (2 2$10 20 30 40) jappend 'mydata' 4
read file. The right argument is the filename, linked with one or more component numbers. The result has the same shape as the component numbers. For example:
> jread 'mydata';4 10 20 30 40
jread 'mydata';i.5 +------+----+----+-------------+-----+ |header|rec1|rec2|+-+---+-----+|10 20| | | | ||1|1 2|1 2 3||30 40| | | | |+-+---+-----+| | +------+----+----+-------------+-----+ jread 'mydata';i.2 2 +------+-------------+ |header|rec1 | +------+-------------+ |rec2 |+-+---+-----+| | ||1|1 2|1 2 3|| | |+-+---+-----+| +------+-------------+
replace in file. The result is the components replaced. For example:
(1000;'abcde') jreplace 'mydata';1 2 1 2 jread 'mydata';i.3 +------+----+-----+ |header|1000|abcde| +------+----+-----+
The left argument is reshaped if necessary to match the components in the right argument. For example, the following replaces components 1-3, each with the word
'reserved' jreplace 'mydata';1 2 3 1 2 3
duplicate file. The left argument is the new filename; if elided, the file is duplicated in place. The right argument is the source filename, optionally linked with one or more component numbers to be copied to the new file, in the order given. By default, the entire file is duplicated. The result is the number of components written. For example:
'newdata' jdup 'mydata' 5 'newdata' jdup 'mydata';2 0 1 3
size of file, as 4 numbers:
starting component number (0)
number of components
length of file in bytes (same as result of
amount of free space that could be recovered by duplicating the file
jsize 'newdata' 0 5 1312 0
erase file, for example:
jerase 'newdata' 1
You can refer to a file either by its filename, or by its file handle if you have already opened the file. For most purposes we recommend using the filename. However, if you have a great deal of file activity, you may find it faster to open the file first, then use the file handle; this means the system does not have to open and close the file each time it is accessed. The utilities
jclose in the jfiles locale can be used for this purpose. For example:
h=. jopen_files_ 'mydata'
... process file using handle
jclose_files_ h 1
Each file is structured as a header record, followed by data. The header record is as follows:
 starting component
 number of components
 file length
 directory pointer
 freelist pointer
 sequence number
Each component is stored in its 3!:1 representation in a space that is a power of 2. This means that on average, the representation fills 75% of the space allocated, and allows some space for growing replaces. If the space required on replacement is less than half the space allocated, then the balance is freed up.
If you replace a component with a noun of smaller size, then this may result in some unused space. The system keeps track of this in the freelist, and attempts to reuse it where possible. The total free space available is given in the fourth element of the result of
jsize, and this is the space that would be freed by duplicating the file using