There are two completely different approaches to formatting - the first one well object designed with special formatting objects and the possibility to change everything and the second one - more pragmatic - with similar possibilities as the formatting in Languages starting with C (like C, Cobol, Clipper...). As one of our main objectives is to make life more easy for former C and Cobol programmers our preference is on the second possibility.
Both classes - the scientific and the commercial format - rely on FORMAT which gives the more object oriented approach and delivers the base functionality.
The correctness of the formats can not be evaluated at compile time. In fact no format can be checked completely at compile time as the final appearance on paper or on the screen must be verified by a human in each case.
In each case it possible to control at runtime with preconditions:
So the approach is not completely unsafe. Part of the class declaration is a set of routines checking the correctness of the format string and parameters. Of course these routines fit to the class, i.e. in the scientific class they accept only this format and in the commercial version all format strings.
Format specification, which consists of optional and required fields, has the following form:
![flags] [width] [.precision] typechar
Each field of the format specification is a single character or a number signifying a particular format option. The simplest format specification contains only the default escape character and a typechar (for example, !s). To print a default escape character, write it twice (e.g. !!).
The optional fields, which appear before the type character, control other aspects of the formatting, as follows.
Required character that determines whether the associated argument is interpreted as a character, a string, or a number.
Available in class SCIENTIFIC_FORMAT typechars are the following:
Table 1.
c | CHARACTER | Single character |
---|---|---|
b | BOOLEAN | "true" or "false" strings |
s | STRING | Character string; Characters are printed up to the end of string or until the precision value is reached |
d | INTEGER | Signed decimal integer |
i | INTEGER | Signed decimal integer |
o | INTEGER | Unsigned octal integer |
x | INTEGER | Unsigned hexadecimal integer, using "abcdef" |
u | INTEGER | Unsigned decimal integer |
e | REAL | Signed value in the form "[-]d.dddd e [sign]ddd" where d is a single decimal digit, dddd is one or more decimal digits, ddd is exactly three decimal digits, and sign is + or - |
f | REAL | Signed value having the form "[-]dddd.dddd" where dddd is one or more decimal digits. The number of digits before the decimal point depends on the magnitude of the number, and the number of digits after the decimal point depends on the requested precision |
g | REAL | Signed value printed in f or e format, whichever is more compact for the given value and precision. The e format is used only when the exponent of the value is less than -4 or greater than or equal to the precision argument. Trailing zeros are truncated, and the decimal point appears only if one or more digits follow it |
t | TIME | a time output (e.g. "17:15:00") can be cut by precision |
a | DATE | a date output (e.g. "01/09/95") can be cut by precision |
Note that this list can be extended. Note also that here only letters between a .. z are legal - capitals and other symbols are used for picture formats (see below).
An optional character or characters that control justification of output and printing of signs, blanks. More than one flag can appear in a format specification, but they must be compatible to each other.
Table 2.
Flag | Meaning | Default |
---|---|---|
^ | Center result within the given fields width | Right align |
- | Left align the result within the given field width | Right align |
+ | Prefix the output value with a sign (+ or -) if the output value is of a signed type | Sign appears only for negative signed values (-) |
0 | Zeros are added until the minimum width is reached | No padding |
' ' (blank) | Prefix the output value with a blank if the output value is signed and positive | No blank appears |
Note: flags combination (' ' after +) and (0 with -,0 with ^, and - with ^) are incorrect.
An optional number that specifies the minimum number of characters output.
The width argument is a non-negative decimal integer controlling the minimum number of characters printed. If the number of characters in the output value is less than the specified width, blanks are added to the left or the right of the values - depending on whether the - flag (for left alignment) is specified - until the minimum width is reached. If width is prefixed with 0, zeros are added until the minimum width is reached (not useful for left-aligned numbers).
The width specification never causes a value to be truncated. If the number of characters in the output value is greater than the specified width, or if width is not given, all characters of the value are printed (subject to the precision specification).
If the width specification is an asterisk (*), an int argument from the argument list supplies the value. The width argument must precede the value being formatted in the argument list. A nonexistent or small field width does not cause the truncation of a field; if the result of a conversion is wider than the field width, the field expands to contain the conversion result.
The third optional field of the format specification is the precision specification. It specifies a non-negative decimal integer, preceded by a period (.), which specifies the number of characters to be printed, the number of decimal places, or the number of significant digits (see table below). Unlike the width specification, the precision specification can cause either truncation of the output value or rounding of a floating-point value. If precision is specified as 0 and the value to be converted is 0, the result is a blank string, as shown below:
printf ("!.0d", <<0>>) -- ... no characters are output
If the precision specification is an asterisk (*), an int argument from the argument list supplies the value. The precision argument must precede the value being formatted in the argument list.
Table 3. How the precision values can affect type.
Type | Meaning | Default | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
c | The precision has no effect | Character is printed | ||||||||
b | The precision has no effect | "true"/"false" is printed | ||||||||
d, i, u, o, x | The precision specifies the minimum number of digits to be printed. If the number of digits in the argument is less than precision, the output value is padded on the left with zeros | The value is not truncated when the number of digits exceeds precision. Default precision is 1 | ||||||||
e | The precision specifies the number of digits to be printed after the decimal point. The last printed digit is rounded | Default precision is 6; if precision is 0 or the period (.) appears without a number following it, no decimal point is printed | ||||||||
f | The precision value specifies the number of digits after the decimal point. If a decimal point appears, at least one digit appears before it. The value is rounded to the appropriate number of digits | Default precision is 6; if precision is 0, or if the period (.) appears without a number following it, no decimal point is printed | ||||||||
g | The precision specifies the maximum number of significant digits printed | Six significant digits are printed, with any trailing zeros truncated. | ||||||||
s | The precision specifies the maximum number of characters to be printed. Characters in excess of precision are not printed | Characters are printed until end of string | ||||||||
a, t | The precision specifies how many characters of time/date printed, e.g.:
|
Full 8-character string (e.g. "13/01/95") |
Value | Format | Result |
---|---|---|
Hello,·John |
10s |
Hello,·Joh |
-10.5s |
Hello····· |
|
^10.5s |
··Hello··· |
|
10.5s |
·····Hello |
|
s |
Hello,·John |
|
3.1245621 |
5d |
····3 |
-3d |
3·· |
|
6.3f |
·3.124 |
|
8e |
3.124562e+00 |
An alternative way of formatting is by using special patterns (pictures, templates). We think that for all types, except fixed point numbers this is an obsolete format. On developer's opinion with help of scientific format you can produce more flexible formatting of your data. To satisfy any customer of the library we realize subset of pattern print, based on COBOL and PL/1 formats also.
As we use capital letters for templates it is easy to distinguish between the C-like scheme and the pattern. Look at types and characters allowable in them:
X | Any character |
---|---|
I | Any character, but centered in field |
You can use only one type of characters in string picture: either X or I.
The next feature is repeatance. Construction X(n)
where n is positive
number mean repeat X
n times. For example X(7)
means XXXXXXX
.
If you write X(0)
it means float length of pattern, it means that the length
of output is equal to length of string. I(0)
is allowed but useless.
Also you can follow pictures with the following commands:
[JUSTIFIED RIGHT] [JUSTIFIED LEFT] [UPPER CHARACTER] [LOWER CHARACTER]
These commands set justification and character case of picture string.
9 | Digit 0-9 |
---|---|
B | Blank |
- | Show '-' if negative, ' ' (blank) if positive |
+ | Show '-' if negative, '+' if positive |
Z | Show a leading or a trailing zero in this position as a space |
* | Show a leading or trailing zero in this position as a cheque protection mark |
. | Show the local decimal point symbol |
, | Show the local digit separator symbol |
$ | Show currency symbol here |
Characters, such as Z * + - can be "floating". It means that picture ZZZZ999
is interpreted as "type at least three digits of the number, if their quantity in the
number is less then 7 you can type leading zeros as blank". Examples:
number 1234 999999 --> 001234 ZZZZZZ --> 1234 ****99 --> **1234 number -1234 99999- --> 01234- -99999 --> -01234 ------ --> -1234
So, the main rules are:
9
can be before any floating picture character after decimal point you
can use only 9
, sign and the currency sign at the last position9(0)
is not allowed. It has no meaning. The best way to print your number
with float length is to use a scientific format. But you can use 9(5)
B
is used as the "noise character". It can be freely
inserted into any picture. It simply prints space.You also can use the following commands after a picture:
[BLANK WHEN ZERO] [SIGN XYZ] [SIGN XYZ SEPARATE]
where XYZ
can be either LEADING
or TRAILING
The first command above acts on 9 like Z. The second and third ones can
be used to append sign character + to picture. Keyword SEPARATE
is
used to make it as a standalone character - absence of this word makes Z as
floating +.
There are only two allowed picture characters: L and Y. L prints True/False as T/F and Y as Y/N. No more characters you can use in this picture.
"Picture between two nine's 9!(999)9")
printf ("!999!i",<<123,123>>>))
set_default_format ("!99999") print (123) -- use picture "99999" when print out INTEGER numbers set_default_format ("!-d") print (123) -- use SCIENTIFIC format "-d"
printf ("!999", <<"abc">>)
an exception "Incorrect picture character" - string picture handler got '9' will be produced.
routine_testing_formats is local fmt: G_COMMERCIAL_FORMAT x : TIMESTAMP date : TIMESTAMP s : STRING do !! fmt.make !! x.set_local_time !! date.set_local_time !! s.make_empty fmt.printf ("!d !10s !05x !8.8t !8.8a%N", <<1234, "Hello-123456", 20, x, x>>) -- result: 1234 Hello-123456 00014 15:09:21 03/20/02 fmt.set_default_format ("10d") fmt.print (1234) io.put_new_line -- result: ______1234 fmt.set_default_format ("010d") fmt.print (1234) io.put_new_line -- result: 0000001234 fmt.printf ("!d !d !s", <<1, 2, "Hallo">>) io.put_new_line -- result: 1 2 Hallo -- UPPER doesn't work fmt.printf ("!9999!9!XX[UC]", <<1, 2, "Hallo">>) -- result: 00012HA io.put_new_line s := "abcdef" s.append_string (fmt.sprint(123)) s.append_string (fmt.to_string("!d", 456)) io.put_string(s) io.put_new_line -- result: abcdef0000000123456 -- Mixing of both formats is allowed: fmt.printf ("!10d ![BZ]+9999%N", <<1234,1234>>) -- result: 1234 +1234 -- !999 doesn't work fmt.printf ("!9999 !10t", <<1234, date>>) -- result: 1234 15:09:21 end
The creation procedure 'make' initializes all default values to some acceptable value - and so it may be possible to define user specific format classes like
class GERMAN_BUSINESS_FORMAT inherit COMMERCIAL_FORMAT rename make as commercial_make creation make feature make is do commercial_make set_decimal(',') set_default_format ("**.***.**9,99 $$") set_currency ("DM") end