worley@compass.com (Dale Worley) (10/04/90)
I was writing a script and wanted to have fixed-field numeric output
in a report, but you can only do that by putting sprintf() calls into
the format, which is inelegant. So I wrote a small modification to
Perl to allow fields with numeric format specifications like:
@####
@##.##
@###.
@.###
The @ and # are digit placeholders, the . is the decimal point
placeholder. The output is done with C's sprintf using a format like
%6.2f or %#6.2f, so the number should be correctly rounded to the
number of places you specify.
The field value you give is interpreted as a number in the usual Perl
way, except that if the value is undefined and the field is started
with ^ rather than @, then the field prints as blanks.
The code is given below as diffs from PL 28. I've only tested it on a
few cases, but it seems to be working. Any comments are welcome!
Dale Worley Compass, Inc. worley@compass.com
--
My favorite was an example in Dijkstra's classic "A Discipline of
Programming" where he claimed that he hadn't submitted his program
to operational testing since it had been created using a discipline
that guaranteed it would be correct. Naturally, there were a couple
of bugs in it! -- Doug Gwyn
*** base-toke.c Tue Aug 14 10:31:09 1990
--- toke.c Wed Oct 3 10:40:54 1990
***************
*** 2254,2260 ****
--- 2254,2288 ----
while (*s == '|')
s++;
break;
+ case '#':
+ case '.':
+ /* Catch the special case @... and handle it as a string
+ field. */
+ if (*s == '.' && s[1] == '.') {
+ goto default_format;
+ }
+ fcmd->f_type = F_DECIMAL;
+ {
+ char *p;
+
+ /* Read a format in the form @####.####, where either group
+ of ### may be empty, or the final .### may be missing. */
+ while (*s == '#')
+ s++;
+ if (*s == '.') {
+ s++;
+ p = s;
+ while (*s == '#')
+ s++;
+ fcmd->f_decimals = s-p;
+ fcmd->f_flags |= FC_DP;
+ } else {
+ fcmd->f_decimals = 0;
+ }
+ }
+ break;
default:
+ default_format:
fcmd->f_type = F_LEFT;
break;
}
*** base-form.h Thu Oct 19 17:02:57 1989
--- form.h Wed Oct 3 10:38:44 1990
***************
*** 16,21 ****
--- 16,22 ----
#define F_RIGHT 2
#define F_CENTER 3
#define F_LINES 4
+ #define F_DECIMAL 5
struct formcmd {
struct formcmd *f_next;
***************
*** 25,30 ****
--- 26,32 ----
char *f_pre;
short f_presize;
short f_size;
+ short f_decimals;
char f_type;
char f_flags;
};
***************
*** 33,38 ****
--- 35,41 ----
#define FC_NOBLANK 2
#define FC_MORE 4
#define FC_REPEAT 8
+ #define FC_DP 16
#define Nullfcmd Null(FCMD*)
*** base-form.c Mon Aug 13 18:53:36 1990
--- form.c Wed Oct 3 11:18:00 1990
***************
*** 281,286 ****
--- 281,311 ----
d += size;
linebeg = fcmd->f_next;
break;
+ case F_DECIMAL: {
+ double value;
+
+ (void)eval(fcmd->f_expr,G_SCALAR,sp);
+ str = stack->ary_array[sp+1];
+ /* If the field is marked with ^ and the value is undefined,
+ blank it out. */
+ if ((fcmd->f_flags & FC_CHOP) && !str->str_pok && !str->str_nok) {
+ while (size) {
+ size--;
+ *d++ = ' ';
+ }
+ break;
+ }
+ value = str_gnum(str);
+ size = fcmd->f_size;
+ CHKLEN(size);
+ if (fcmd->f_flags & FC_DP) {
+ sprintf(d, "%#*.*f", size, fcmd->f_decimals, value);
+ } else {
+ sprintf(d, "%*.0f", size, value);
+ }
+ d += size;
+ break;
+ }
}
}
CHKLEN(1);