165 lines
8.0 KiB
Plaintext
165 lines
8.0 KiB
Plaintext
BEGIN # Test processing/2 - using thee file from Text processing/1, #
|
|
# verify the format, detect duplicate dates and #
|
|
# count the number of records with all instruments #
|
|
# functioning #
|
|
|
|
PR read "files.incl.a68" PR # include file utilities: EACHLINE, etc. #
|
|
|
|
CHAR tab = REPR 9;
|
|
|
|
INT instruments = 24; # expected number of instrument readings #
|
|
MODE READING = STRUCT( STRING date
|
|
, [ 1 : instruments ]REAL data
|
|
, [ 1 : instruments ]INT status
|
|
, BOOL format ok
|
|
, STRING message
|
|
);
|
|
OP INIT = ( REF READING r )VOID:
|
|
BEGIN
|
|
FOR n TO instruments DO ( data OF r )[ n ] := ( status OF r )[ n ] := 0 OD;
|
|
format ok OF r := TRUE;
|
|
date OF r := "";
|
|
message OF r := ""
|
|
END # INIT # ;
|
|
|
|
INT all good count := 0; # number of lines which have all instruments ok #
|
|
STRING last date := ""; # date of the previous line #
|
|
|
|
# return the date and instrument readings and status values on the line #
|
|
PROC parse readings = ( STRING line )READING:
|
|
BEGIN
|
|
BOOL data error := FALSE;
|
|
# returns v converted to a REAL, returns 0 and sets data error #
|
|
# to TRUE if v is invalid #
|
|
OP TOREAL = ( STRING v )REAL:
|
|
BEGIN
|
|
REAL value := 0;
|
|
FILE real value;
|
|
associate( real value, LOC STRING := v );
|
|
on value error
|
|
( real value
|
|
, ( REF FILE ef )BOOL: # "handles" invalid data and #
|
|
BEGIN # returns TRUE so processing #
|
|
value := 0; # can continue #
|
|
data error := TRUE
|
|
END
|
|
);
|
|
get( real value, value );
|
|
close( real value );
|
|
value
|
|
END # TOREAL # ;
|
|
# returns v converted to an INT, returns 0 and sets data error #
|
|
# to TRUE if v is invalid #
|
|
OP TOINT = ( STRING text )INT:
|
|
BEGIN
|
|
INT value := 0;
|
|
BOOL is numeric := TRUE;
|
|
FOR ch pos FROM UPB text BY -1 TO LWB text WHILE is numeric DO
|
|
CHAR c = text[ ch pos ];
|
|
IF c = "-"
|
|
THEN value := - value;
|
|
is numeric := ch pos = LWB text AND ch pos /= UPB text
|
|
ELSE is numeric := is numeric AND c >= "0" AND c <= "9";
|
|
IF is numeric THEN ( value *:= 10 ) +:= ABS c - ABS "0" FI
|
|
FI
|
|
OD;
|
|
IF NOT is numeric THEN data error := TRUE FI;
|
|
value
|
|
END # TOINT # ;
|
|
# superficially checks v is in the format yyyy-mm-dd #
|
|
PROC check date format = ( STRING v )BOOL:
|
|
BEGIN
|
|
STRING ymd := v[ AT 1 ];
|
|
IF NOT ( data error := UPB ymd /= 10 ) THEN
|
|
# length is correct for yyyy-mm-dd #
|
|
IF NOT ( data error := ymd[ 5 ] /= "-" OR ymd[ 8 ] /= "-" ) THEN
|
|
INT d;
|
|
d := ( TOINT ymd[ 1 : 4 ] * 10000 )
|
|
+ ( TOINT ymd[ 6 : 7 ] * 100 )
|
|
+ TOINT ymd[ 9 : 10 ]
|
|
FI
|
|
FI;
|
|
NOT data error
|
|
END # check date format # ;
|
|
|
|
READING result; INIT result;
|
|
INT c pos := LWB line;
|
|
INT max pos = UPB line;
|
|
INT field number := 0;
|
|
WHILE c pos <= max pos DO
|
|
# skip leading spaces and tabs #
|
|
WHILE IF c pos > max pos
|
|
THEN FALSE
|
|
ELSE CHAR ch = line[ c pos ]; ch = " " OR ch = tab
|
|
FI
|
|
DO c pos +:= 1 OD;
|
|
IF c pos <= max pos THEN # have a field #
|
|
INT start pos = c pos;
|
|
WHILE IF c pos > max pos
|
|
THEN FALSE
|
|
ELSE CHAR ch := line[ c pos ]; ch /= " " AND ch /= tab
|
|
FI
|
|
DO c pos +:= 1 OD;
|
|
STRING f := line[ start pos : c pos - 1 ];
|
|
IF ( field number +:= 1 ) = 1 THEN # the date #
|
|
IF NOT check date format( f ) THEN
|
|
format ok OF result := FALSE;
|
|
message OF result := "Invalid date: " + f
|
|
FI;
|
|
date OF result := f
|
|
ELIF INT instrument = field number OVER 2;
|
|
instrument > instruments THEN
|
|
format ok OF result := FALSE; # too many readings #
|
|
message OF result := "Too many instruments"
|
|
ELIF ODD field number THEN
|
|
# field 3, 5, 7... the instrument state of reading #
|
|
( status OF result )[ field number OVER 2 ] := TOINT f;
|
|
IF data error THEN
|
|
format ok OF result := FALSE;
|
|
message OF result := "Invalid status for instrument " + whole( instrument, 0 )
|
|
FI
|
|
ELSE # must be the instrument reading #
|
|
( data OF result )[ field number OVER 2 ] := TOREAL f;
|
|
IF data error THEN
|
|
format ok OF result := FALSE;
|
|
message OF result := "Invalid reading for instrument " + whole( instrument, 0 )
|
|
FI
|
|
FI
|
|
FI
|
|
OD;
|
|
IF format ok OF result AND field number /= 2 * instruments + 1 THEN
|
|
format ok OF result := FALSE;
|
|
message OF result := "Incorrect number of readings/status values"
|
|
FI;
|
|
result
|
|
END # parse readings # ;
|
|
|
|
# checks the readings and date on line #
|
|
PROC check readings = ( STRING line, INT line count )BOOL:
|
|
BEGIN
|
|
READING data := parse readings( line );
|
|
IF format ok OF data AND line count > 1 AND date OF data = last date THEN
|
|
format ok OF data := FALSE;
|
|
message OF data := "Duplicate date: " + date OF data
|
|
FI;
|
|
IF NOT format ok OF data THEN # line has invalid data #
|
|
print( ( "Line: ", whole( line count, 0 ), ": ", message OF data, newline ) )
|
|
ELSE # the line appears ok #
|
|
BOOL all good := TRUE;
|
|
FOR i TO instruments WHILE all good := ( status OF data )[ i ] > 0 DO SKIP OD;
|
|
IF all good THEN all good count +:= 1 FI;
|
|
last date := date OF data
|
|
FI;
|
|
TRUE
|
|
END # check readings # ;
|
|
|
|
IF STRING file name = "readings.txt";
|
|
INT total lines = file name EACHLINE check readings;
|
|
total lines < 0
|
|
THEN print( ( "Unable to open ", file name, newline ) )
|
|
ELSE print( ( whole( total lines, 0 ), " lines in ", file name, ", " ) );
|
|
print( ( whole( all good count, 0 ), " have all instruments in a good state.", newline ) )
|
|
FI
|
|
|
|
END
|