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);