[ Allan Johansen ]
[ ... ]
> tekasd:9868755 86565 8709707 9765875 875
> sdf:34234 9878097 098787 78676 769
> kurt:1234 89769876 98779 897879 676
> kjlkjlkh:0 07087807 87686 8000 44223
>
> Det jeg gerne vil have ud af dette er ordet "kurt" og tallet "1234"
> lige efter (altid adskilt af kolon), således at hver del bliver
> smidt i hver sin variablen.
Hva avslutter tallet? Whitespace?
> Jeg kan kun komme frem til noget lignende dette:
>
> fp=fopen("/proc/net/dev","r");
> while(!feof(fp))
Denne konstruksjonen tyder oftest på at input'en vil bli lest feil.
Grunnen til dette er at EOF indikator trenger ikke å være satt før
_etter_ at man har lest forbi EOF. Mao., man er nødt til å teste for
hvorvidt inputoperasjonen lyktes likevel, og da blir testen
meningsløs. Som regel mener man:
<consume input item 1>
while ( !feof( ifs ) ) {
<process input item i>
<consume input item i+1>
}
> {
> c = fgetc(fp);
> if(c == ':')
> {
> /*tag det til venstre og smid ind i variabel navn*/
> /*tag det til højre og smid ind i variabel tal*/
> }
>
> }
> fclose(fp);
>
> printf("%s\n", navn);
> printf("%i\n", navn);
Disse printf'ene er litt meningsløse, med tanke på inputstrukturen.
Tja, hva skal man anbefale her?
Variant 1: (egnet for ting som /etc/passwd)
----------
Man definerer en fast grense på hvor stor inputfeltene kan være og man
antar filen er strukturert på linjenivå:
const size_t NAME_LIMIT = 9U;
const size_t KEY_LIMIT = 20U;
const size_t LINE_LIMIT = 1000U;
const char whitespace[] = " \t\v\n";
char name[ NAME_LIMIT ];
char key[ KEY_LIMIT ];
char line[ LINE_LIMIT ];
while ( fgets( line, sizeof line, ifs ) ) {
char *separator = strchr( line, ':' );
size_t name_length = separator - line;
size_t key_length = strcspn( separator, whitespace );
/* error-checking */
memmove( name, line, name_length );
name[ name_length ] = '\0';
memmove( key, separator + 1, key_length );
key[ key_length ] = '\0';
process( name, key );
}
Legg merke til at det mangler feilhåndtering på essensielle steder,
men denne øvelsen er overlatt til leseren.
Variant 2:
----------
Nå tillater vi litt større frihet hva angår størrelsen på navn og
nøkler. Men ting er fortsatt linjeorienterte. Siden man ikke lenger
har et maksimalt antall tegn å jobbe i fra, er vi nødt til å la
datastrukturene våre vokse dynamisk. Det gir heller ikke mening å lese
i "linjer", da vi ikke vet hvor store de er.
Ideen er å simulere en tilstandsmaskin hvor man begynner med å samle
tegn til name fram til man ser ':', og deretter samler vi tegn til key
såfremt det neste tegnet er et siffer:
char *name = allocate();
char *line = allocate();
typedef enum { INNAME, INDIGIT, SKIP } state_t;
int input;
state_t state = INNAME;
while ( (input = fgetc( ifs )) != EOF ) {
if ( input == '\n' ) {
process( name, key );
reset( name );
reset( key );
state = INNAME;
}
else if ( input == ':' ) {
/* now we have the name */
process_name( name );
state = INDIGIT;
}
else if ( isalpha( input ) && state == INNAME )
append( name, input );
else if ( isdigit( input ) && state == INDIGIT )
append( key, input );
else {
/*
* We have an input character, but it is not part of name or
* key
*/
state = SKIP;
}
}
Man begynner i tilstand "INNAME" -- vi samler opp tegn til navn. Når
vi ser ':', vet vi at navnet er slutt og det skal komme en nøkkel (i
dette tilfellet et tall). Derfor settest tilstanden til INDIGIT. Vi
forblir i den tilstanden inntil vi ser noe annet enn et siffer. Da kan
man sette tilstanden til SKIP og bare lese inn de resterende tegn fra
linjen (det er uinteressant å gjøre noe med de). Når vi så ser '\n',
betyr det at linjen er ferdiglest, de riktige verdiene ligger i name
og line og det er på tide å prosessere de.
Man er nødt til å håndtere vilkårlig lange navn/nøkler, og derfor er
det lurt med hjelpefunksjoner som fikser slikt automatisk. Meningen er
at append() skal ta hånd om det. allocate() lager en string og reset()
"tømmer" den stringen i en passende forstand.
Variant 3: (meget egnet når input'en er litt mer komplisert og/eller
---------- når man er lat)
Vi bruker Lex. Gjerne i kombinasjon med Yacc. Akkurat i dette
tilfellet blir det veldig interessant, fordi du vil gjerne kaste
"resten" av linjen, og det er ikke godt å si hva ligger i den. Jeg går
ut ifra at det går an å fortelle Lex at den skal kaste vekk alt etter
nøkkelen, men da er ISBN 1-565-92000-7 (og evt.
<URL:
http://www.opengroup.org/onlinepubs/007904975/utilities/lex.html>
(forresten, er Lex POSIXifisert?)) det riktige stedet å slå opp.
> Er der nogle der kan hjælpe/se hvad jeg mangler, eller kender et
> sted på nettet, hvor jeg kan læse om dette?
I det generelle tilfellet er:
@Book{misc:CPT86,
author = {{Aho, Alfred V.} and {Sethi, Ravi} and {Ullman, Jeffrey D.}},
title = {Compilers - Prin\-ci\-ples, Techniques, and Tools},
publisher = AW,
address = {Reading, MA, USA},
pages = {x + 796},
year = {1986},
ISBN = {0-201-10088-6},
LCCN = {QA76.76.C65 A371 1986}
}
.... å anbefale. I tilfellet ditt er det også lurt å ta en titt på ISBN
0-131-10362-8 (evt. <URL:
http://www.accu.org/>).
ivr
PS: Koden er ikke blitt kompilert eller testet
--
<peder> igorr: tcl ja... det er fra de dypeste avgrunnene i helvete det...
<peder> php er bare fra foajeen
-- pederst på irc