ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы.



11.4 More About the Fieldholders

So far, by example, you know that the fieldholder @<<<< indicates a 5-character, left-justified field and that @<<<<<<<<<< indicates an 11-character, left-justified field. Here's the whole scoop, as promised earlier.

11.4.1 Text Fields

Most fieldholders start with @. The characters following the @ indicate the type of field, while the number of characters (including the @) indicates the field width.

If the characters following the @ are left-angle brackets (<<<<), you get a left-justified field; that is, the value will be padded on the right with spaces if the value is shorter than the field width. (If a value is too long, it's truncated automatically; the layout of the format is always preserved.)

If the characters following the @ are right-angle brackets (>>>>), you get a right-justified field - that is, if the value is too short, the field gets padded on the left with spaces.

Finally, if the characters following the @ are vertical bars (||||), you get a centered field; if the value is too short, the field gets padded on both sides with spaces, enough on each side to make the value mostly centered within the field.

11.4.2 Numeric Fields

Another kind of fieldholder is a fixed-precision numeric field, useful for those big financial reports. This field also begins with @, and is followed by one or more #'s with an optional dot (indicating a decimal point). Once again, the @ counts as one of the characters of the field. For example:

format MONEY =
Assets: @#####.## Liabilities: @#####.## Net: @#####.##
$assets, $liabilities, $assets-$liabilities
.

The three numeric fields allow for six places to the left of the decimal place, and two to the right (useful for dollars and cents). Note the use of an expression in the format - perfectly legal and frequently used.

Perl provides nothing fancier than this; you can't get floating currency symbols or brackets around negative values or anything interesting. To do so, you have to write your own spiffy subroutine, like so:

format MONEY =
Assets: @<<<<<<<<< Liabilities @<<<<<<<< Net: @<<<<<<<<<
pretty($assets,10), pretty($liab,9), pretty($assets-$liab,10)
.

sub pretty {
        my($n,$width) = @_;
        $width -= 2; # back off for negative stuff
        $n = sprintf("%.2f",$n); # sprintf is in later chapter
        if ($n < 0) {
            return sprintf("[%$width.2f]", -$n);
                # negative numbers get brackets
        } else {
            return sprintf(" %$width.2f ", $n);
                # positive numbers get spaces instead
        }
}

## body of program:
$assets = 32125.12; 
$liab = 45212.15; 
write (MONEY);

11.4.3 Multiline Fields

As mentioned earlier, Perl normally stops at the first newline of a value when placing the result into the output. One kind of fieldholder, the multiline fieldholder, allows you to include a value that may have many lines of information. This fieldholder is denoted by @* on a line by itself; as always, the following line defines the value to be substituted into the field, which in this case may be an expression that results in a value containing many newlines.

The substituted value will look just like the original text: four lines of value become four lines of output. For example:

format STDOUT =
Text Before.
@*
$long_string
Text After.
.

$long_string = "Fred\nBarney\nBetty\nWilma\n";
write;

generates the output:

Text Before.
Fred
Barney
Betty
Wilma
Text After.

11.4.4 Filled Fields

Another kind of fieldholder is a filled field. This fieldholder allows you to create a filled paragraph, breaking the text into conveniently sized lines at word boundaries, wrapping the lines as needed. There are a few parts that work together here, but let's look at them separately.

First, a filled field is denoted by replacing the @ marker in a text fieldholder with a caret (so you get ^<<<, for example). The corresponding value for a filled field (on the following line of the format) must be a scalar variable[3] containing text, rather than an expression that returns a scalar value. The reason for this is that Perl will alter the variable while filling the filled field, and it's pretty hard to alter an expression.

[3] The scalar value can include a single scalar element of an array or hash, like $a[3] or $h{"fred"}.

When Perl is filling the filled field, it takes the value of the variable and grabs as many words (using a reasonable definition of "word")[4] as will fit into the field. These words are actually ripped out of the variable - the value of the variable after filling this field is whatever is left over after removing the words. You'll see why in a minute.

[4] The word-separator characters are defined by the $: variable.

So far, this isn't much different from how a normal text field works; we're printing only as much as will fit (except that we're respecting a word boundary rather than just cutting it off at the field width). The beauty of this filled field appears when you have multiple references to the same variable in the same format. Take a look at this:

format PEOPLE =
Name: @<<<<<<<<<<<<< Comment: ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
      $name,                  $comment
                              ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                              $comment
                              ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                              $comment
                              ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                              $comment
.

Note that the variable $comment appears four times. The first line (the one with the $name field) prints the person's name and the first few words of the value in $comment. But in the process of computing this line, $comment is altered so that the words disappear. The second line once again refers to the same variable ($comment), and will take the next few words from the same variable. This process is also used for the third and fourth lines. Effectively, what we've created is a rectangle in the output that will be filled as best it can with the words from $comment spread over four lines.

What happens if the complete text occupies less than four lines? Well, you'll get a blank line or two. This result is probably OK if you are printing out labels and need exactly the same number of lines for each entry to match them up with the labels. But if you are printing out a report, many blank lines merely use up your printer's paper budget.

To fix this, use the suppression indicator. Any line that contains a tilde (~) character is suppressed (not output) if the line would have otherwise printed blank (just whitespace). The tilde itself always prints as a blank and can be placed anywhere a space could have been placed in the line. We could rewrite that last example as follows:

format PEOPLE =
Name: @<<<<<<<<<<<<< Comment: ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
      $name,                  $comment
~                             ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                              $comment
~                             ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                              $comment
~                             ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                              $comment
.

Now, if the comment covers only two lines, the third and fourth lines are automatically suppressed.

What if the comment is more than four lines? Well, we could make about 20 copies of the last two lines of that format, hoping that 20 lines will suffice. But that goes against the idea that Perl helps you to be lazy, so there's a lazy way to do it. Any line that contains two consecutive tildes will be repeated automatically until the result is a completely blank line. (The blank line is suppressed.) This changes our format to look like this:

format PEOPLE =
Name: @<<<<<<<<<<<<< Comment: ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
      $name,                  $comment
~~                            ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                              $comment
.

This way, if the comment takes 1 line, 2 lines, or 20 lines, we are still OK.

Note that the criterion for stopping the repeated line requires the line to be blank at some point. That means you probably don't want any constant text (other than blanks or tildes) on the line, or else the line will never become blank.