String Templates

For BBj-specific information, see String Templates - BBj.

Overview

PRO/5 allows a type of record descriptor called a template to be associated with a string variable. The DIM statement may be used to associate a template with any string variable. For example, the statement below uses B$ as the template for the string variable A$. The template (B$ in this case) can be given a value that describes one or more fields in the string variable.

1000 DIM A$:B$

The statements below are an example of setting up a template and associating the template with a variable. Line 0900 describes two fields called NAME and AGE. The field types (indicated by "C" and "N") describe the form of data within each field. The number in parentheses specifies the length of the field. The NAME field is a character string ten bytes long, and the AGE field is a numeric string three bytes long.

0900 LET B$="NAME:C(10),AGE:N(3)"

1000 DIM A$:B$

Once a string is defined with a template, its fields may be accessed by name. The name takes the format:

variable.field

A name may be a string of up to 32 alphanumeric characters. Since the variable and the delimiting period are required, the maximum length for field is 30 characters (assuming a one-character variable name).

An additional field name, used for matching external formats is "*". This name may be used to define fields or for fill space.

An example of accessing a field of a variable by name is:

1000 LET C$=A.NAME$

This example accesses the field called "NAME" in the string variable called A$ (more about this later).

The field type ("C" and "N" in the above example) describes the form of data within that field. The following types are supported:

Data Type

Description

B

Business floating point (8 bytes), math format for PRO/5

C

Simple character string

D

BCD floating point value (8 bytes)

F

IEEE binary floating point value (8 bytes)

I

Signed binary integer (1 to 6 bytes)

N

Numeric string

U

Unsigned binary integer (1 to 6 bytes)

X

Local float (C) value (4 bytes)

Y

Local double (C) value (8 bytes)

Some field types have a specific length, and attempting to include a length specification in definitions of these field types will generate an !ERROR=20. The field types with specific lengths are F, D, B, X, and Y. The following example defines a Business Math field:

DIM B$:"value:B"

The length designation of a template field may specify either fixed or variable length. The allowable range of lengths depends on the type of field, as shown above. Variable length fields are indicated by an asterisk in the length designation, for example:

DIM B$:"NAME:C(10*)"

The asterisk means that the field is terminated with a linefeed ($0A$). In this case the "10" is not explicitly used by PRO/5 but may be useful to applications (such as a database manager). If a field terminator other than a linefeed is required, then it may be specified as shown below:

DIM B$:"NAME:C(10*=0)"

In the above example, a null is given as a terminator. Any value from 0 to 255 may be given as a terminator value. Finally, if the end of the field is to be the end of the string itself (with no terminator), then it may be declared as follows:

DIM B$:"NAME:C(10*=)"

NOTE: You should not attempt to use terminated fields with records containing binary data, that is, data other than "C" and "N" types.

Fixed-length fields are always padded (with spaces) or truncated to the defined length. For example:

DIM B$:"NAME:C(10)"

In the above example, any value assigned to B.NAME$ is truncated to 10 characters or padded to 10 characters with spaces, as needed.

Data type C also supports the following syntax (fixed-length, padded):

DIM B$:"NAME:C(10**=0)"

In the above example, the NAME field is defined as a fixed length of 10 characters, padded with $00$ characters.  Trailing pad characters are discarded when the field is retrieved. For example:

DIM FIXED$:"NAME:C(10)"
DIM PADDED$:"NAME:C(10**=32)"

LET FIXED.NAME$="Smith"

LET PADDED.NAME$="Jones"

In the above example, FIXED.NAME$ returns "Smith " while PADDED.NAME$ returns "Jones".

After dimensioning a string with a template, the string data itself is initialized to meaningful data representing an empty template. Character fields will contain spaces, numeric fields will contain zeroes, and variable length fields will be empty.

User-defined Field Attributes

A user-defined attribute string may be optionally given following the length specification:

"NAME:C(10):access=5 mask=###XX## opt=y:"

The colon following the length specification begins the user-defined options. All text up to the next colon is remembered exactly as given. Any text may be used, but a "keyword=value" format (as used above) is recommended in order to use some of the features described later.

Repeating Fields

A field array may be defined:

"SALES[5]:N(10)"

The bracketed value in the above example means that SALES is 5 fields (1..5) - each a numeric. Note that the array is one-based. Only one-dimensional arrays are supported.

Example

The following example defines a template that describes the string returned by the FID() function:

DIM FID$:"TYPE:I(1),KEYSIZ:I(1),RECS:I(4),RECSIZ:I(2),NAME:C(64*=)"

TYPE and KEYSIZ are 1-byte integers. RECS is a 4-byte integer. RECSIZ is a 2-byte integer. NAME is a variable-length character string that is the remainder of the string.

Byte and Word Swapping

Some non-PRO/5 file formats contain binary data in byte-swapped or word-swapped forms. Swapping may be specified as follows:

C$="COUNT:I/BWL(4)"

The slash following the field type indicates that swapping options follow. The letters B (byte), W (word), and L (long) may be entered in any combination to specify swapping. The table below depicts each option.

 

Long

Long

Swap

Word

Word

Word

Word

Option

Byte

Byte

Byte

Byte

Byte

Byte

Byte

Byte

None

0

1

2

3

4

5

6

7

B

1

0

3

2

5

4

7

6

W

2

3

0

1

6

7

4

5

L

4

5

6

7

0

1

2

3

BW

3

2

1

0

7

6

5

4

BL

5

4

7

6

1

0

3

2

WL

6

7

4

5

2

3

0

1

BWL

7

6

5

4

3

2

1

0

Swapping is only valid for certain field types. The following table contains the allowable field types and options supported for each type:

Type

Description

Swap?

Len

Min

Max

Variable?

C

character string

n

var

1

32767

y

N

numeric

n

var

1

32767

y

I

integer

y

var

1

6

n

U

unsigned integer

y

var

1

6

n

F

IEEE float (8 bytes)

y

8

n/a

n/a

n

D

BCD float (8 bytes)

y

8

n/a

n/a

n

B

Business float (8 bytes)

y

8

n/a

n/a

n

X

local float

y

4

n/a

n/a

n

Y

local double

y

8

n/a

n/a

n

Field types X and Y are stored according to the local swapping rules. When transferring either of these field types between machines, they should be copied to a field type that is not affected by swapping (e.g. type F). Alternatively, the SWAP() function can be used on the sending side to convert local byte order to network byte order, then again on the receiving side to convert network byte order to local byte order.

Accessing Fields of a String Variable

As noted earlier, once a string variable is defined with a template, its fields may be accessed by name:

LET B$=A.NAME$

Previously we saw that the statement above accesses the field called "NAME" in A$. Note that the "$" is not explicitly given following the "A". The "$" at the end of the fieldname means that we want to access it as a string. Any field in a template may be accessed as a string even if it is defined as a numeric. For example:

LET B$=A.RECSIZ$

This would simply transfer the 2-byte field called "RECSIZ" into B$. This would also bypass any byte swapping attributes. In addition, numeric fields may also be accessed as a numeric variable:

LET X=A.RECSIZ

or

LET X=A.RECSIZ%

These 2 examples produce the same result. In the first example PRO/5 will convert the field RECSIZ to a PRO/5 numeric (using a conversion similar to DEC()). In the second example, it will convert RECSIZ into a PRO/5 integer.

If a string is defined with a template it may still be used as any string variable:

PRINT A$(4,6)

However, this may defeat the purpose of the template. Accessing the string itself ignores the template. Note the following example:

LET B$=A$

The contents of A$ will be copied to B$ but not the template. B$ may have its own template or may be a simple string variable.

Template fields are best thought of as a form of substring specifications and may be used any place a substring may be used. If a field is passed through a CALL statement to a public program, it is treated as a call-by-value. A simple string variable passed through an ENTER statement will also pass its template (if there is one).

FATTR() Function

Information about a template may be accessed with the FATTR() function. This function has several forms. The first form returns a template description string that may be used in a DIM statement:

LET B$=FATTR(A$)

This example copies the template description of A$, as data, to B$. An error is issued if A$ does not have a template. For example, if you wanted to define B$ to have the same fields as A$ you would do it this way:

DIM B$:FATTR(A$)

Sometimes it is useful to have a simple list of field names from a template. This could be done as:

LET B$=FATTR(A$,"")

Notice the second, null argument. This will place into B$ a list of field names from A$, each terminated with a linefeed.

Information about a specific field may be determined in the following manner:

LET B$=FATTR(A$,"RECSIZ")

This example will place an encoded field description into B$ that is described below. See Field Attribute Format.

Finally, user-defined attributes may be accessed:

DIM A$:"RECSIZ:I(2):access=5 mask=##,##0:"
LET B$=FATTR(A$,"RECSIZ","mask")

This example will search the user-defined portion of RECSIZ for the keyword "mask" and return the value. An error is generated if no such attribute was given.

FIELD() and NFIELD() Functions

Generally, field names are hardcoded in applications, but some applications, such as database managers and report generators, can benefit from being able to determine field names at run time. The FIELD() and NFIELD() functions allow this flexibility.

To access character (string) data, use FIELD(). To access numeric data, use NFIELD().

Both functions take a string variable (containing a template), an ASCII field name, and an optional index (for field arrays):

LET B$=FIELD(A$,"NAME")
LET X=NFIELD(A$,"SALES",4)

These two examples do the same thing as:

LET B$=A.NAME$
LET X=A.SALES[4]

The difference is that the FIELD() functions allow the field names to be determined at run time - a useful tool for database managers and report generators. A special case of the FIELD() function takes only a string variable:

LET B$=FIELD(A$)

This will cause any extra data at the end of A$ to be discarded. For example, if A$ were read from a file as in:

READ RECORD(1)A$

It may contain the extra nulls used to pad the record. The use of FIELD(A$) will return only that portion of A$ accounted for by the template. This is important if A$ were to contain any variable length fields. These fields could be made larger causing A$ to become larger. A later attempt to WRITE RECORD A$ back to the file would generate an end-of-record condition unless the extraneous pad characters were removed.

FIELD Verb

The counterpart to the FIELD() functions is the FIELD verb, which places data into a field:

FIELD A$,"NAME"="FRED"
FIELD A$,"SALES",[4]=52.50

These two statements are equivalent to:

LET A.NAME$="FRED"
LET A.SALES[4]=52.50

For additional information, see the Field Attribute Format.

Performance Considerations

Accessing a string through a template is generally not as efficient as using a hardcoded substring reference. Likewise, a numeric variable can be accessed faster than a numeric field defined in a templated string because the numeric variable must be located and then converted from string form to an internal numeric form. The programmer should keep this difference in mind in situations where processing speed is an issue. For example using a numeric field to control a FOR/NEXT loop is not efficient. The loop will execute faster if a numeric variable is first set to the value of the numeric field and the numeric variable is used to control the loop.

Using Templates

The template feature is intended for use with data dictionaries and database managers where the format of the user's data is not always known in advance. As an example, consider a simple report generator. The user could input the name of a file from which a report is to be produced. The program could access a data dictionary file to locate the desired template describing the record format of the input file. A string is then dimensioned with that template:

DIM A$:TEMPLATE$

The next step would be to present the user with a list of fields contained in the file. The field list can be obtained from:

LET FIELDLIST$=FATTR(A$,"")

Once the fields are selected the program may access them using the FIELD() and NFIELD() functions. The "attribute information" about each field can be determined by:

LET FIELDTYPE$=FATTR(A$,FIELDNAME$)

A template can be used to format an output line on a report. Templates may also be used to access field list names and attributes. For example, the list of field names returned by FATTR() contains several linefeed-terminated names. This list would be easier to access by creating a template for it:

DIM FLIST$:"NAME[100]:C(32*)"
LET FLIST$=FATTR(A$,"")

The above statement defines a variable FLIST$ with a template that can hold up to 100 names and fill it with the list of field names from A$. The third name in the list could be accessed by:

LET X$=FLIST.NAME$[3]

If the list contained only two names, then an error would be generated. Also, if the list contained more than 100 names, only the first 100 names would be accessible. The attributes for the third field could be determined by:

LET TYPE$=FATTR(A$,FLIST.NAME$[3])