[ Thomas L. Christensen ]
[ ... ]
> Jeg har lige pløjet mig igennem Teach Yourself C in 21 days,
Uff, uff!
> Programmet indlæser teksten fra en fil til et område i hukommelsen.
> Herefter udskifter det ';' med '\0' og gemmer adressen på næste tegn
> i en pointer i et array. Hver pointer peger nu på første tegn efter
> hver semikolon. Så langt så godt.
Mao. du vil stykke opp en fil i "linjer" der ';' er linjeslutt samt at
du skal ha en array der hvert i'te element er en peker til i'te linje?
> Problemet er, at jeg gerne vil skabe det her array dynamisk og
> udvidde det i takt med at der findes semikolon i teksten. Jeg har en
> idé om at det kan gøres med realloc(), men jeg har ikke fået det til
> at virke. Problemet er nok at jeg er i tvivl om hvordan jeg skal
> 'deklarere' et array af pointere så det kan udviddes.
Selv om C i sin nyere utgave fikk VLA'er[*], må man nok krype til
korset og bruke de gamle teknikkene -- du vil ha en peker til en rekke
av elementer av type T, der hver T representerer dine linjer. Denne
"rekken" skal kunne utvides dynamisk.
Det første skrittet er å uttrykke dette på engelsk:
T => pointer to char
result => pointer to T, iow pointer to pointer to char
Og så forteller man dette til cdecl[**]:
$ cdecl
Type `help' or `?' for help
cdecl> declare filedata as pointer to pointer to char
char **filedata
cdecl>
Altså, skal man ha "char **filedata". Det neste skrittet er å
konsultere postinger til Chris Torek[***] eller å slå opp i en
skikkelig bok om akkurat dette emnet[****].
> Samtidig har jeg et andet ønske, nemlig at mit array skal være
> to-dimensionelt - derfor de to variabler lines og rows. Skal arrayet
> deklareres på en speciel måde for at gøre det to-dimensionelt?
Som sagt, du vil neppe ha en array, da disse er objekter hvis
størrelse er kjent compile time (man har VLA'er i C99, men jeg tror
ikke det hjelper her).
Det du vil ha, derimot, er en funksjon, som gitt en "filedata" slik
deklarert over, øker den, slik at du kan legge inn en og en linje,
uten å måtte bry deg om masse annet fjas.
Hvis man tegner dette i ASCII art (bruker fast fontbredde), ser dette
slik ut:
datafilen:
[start]
a quick;brown;fox;jumps;over;a;lazy;dog
[end]
ptext
+-------------------------------------------------+
|a\0quick\0brown\0fox\0jumps\0over\0a\0lazy\0dog\0|
+-------------------------------------------------+
^ ^ ^ ^ ^
| | | | |
| | -----+ | |
| / / +---------+ |
| | | | -----------------------------+
| | | / /
+|+|+|+|+ +|+
|||||||| ... |||
+-+-+-+-+ +-+
parray
> #include <stdio.h>
> #include <stdlib.h>
>
> int main()
> {
> FILE *fp;
> long filesize=0L, count=0L;
> int rows=0, lines=0;
> char *ptext, *parray[10];
char *ptext = NULL, **parray = NULL;
Det er litt viktig å initialisere disse.
> if ((fp = fopen("semi.txt", "rb")) == NULL)
> {
> fprintf(stderr, "Error opening semi.txt");
> exit(1);
> }
>
> rewind(fp);
> fseek(fp, 0L, SEEK_END);
> filesize = ftell(fp);
> rewind(fp);
>
> ptext = malloc((size_t) filesize);
/* to compensate for the terminating '\0' */
ptext = malloc( (size_t)filesize + 1UL );
> fread(ptext, sizeof(char), (size_t) filesize, fp);
ptext[ filesize ] = '\0';
> fclose(fp);
Du trenger ikke sizeof( char ), den _skal alltid_ være 1.
Bruk _aldri_ magiske koder à la 13. Dersom du mener '\n', si nettopp
det. 13 er bare slitsomt å se på.
Herfra kan man gjøre det slik -- løper gjennom ptext og teller hvor
mange ';'-linjer det skal være, samtidig som man "vokser" parray:
/* how many elements have been allocated in parray */
size_t allocated = 1UL;
/* how many lines have been processed */
size_t line_count = 0UL;
/* logical line delimiters in the file */
const char delimiters[] = ";\n";
/* allocate 1 line to begin with */
parray = grow_array( parray, allocated );
char *token = strtok( ptext, delimiters );
while ( token ) {
/* there isn't enough space for the next line */
if ( allocated <= line_count ) {
/* double parray in size */
allocated *= 2;
parray = grow_array( parray, allocated );
}
parray[ line_count ] = token;
++line_count;
token = strtok( NULL, delimiters );
}
parray[ line_count ] = NULL;
Utskriften kan ordenes således:
size_t index;
for ( index = 0UL; parray[ index ] != NULL; ++index )
printf( "line %zo: %s\n", index, parray[ index ] );
Evt., dersom kompilatoren dni ikke vet hva %zo er, bruker man %ull:
printf( "line %ull: %s\n", (unsigned long long)index, parray[ index ] );
Du kan naturligvis referere til en enkel bokstav i hver linje som
parray[ line ][ column ] (dog, vær forskiktig med hvilke verdier
column antar). Dette er ikke helt det samme som todimensjonal array,
men kanskje det er tilstrekkelig for dine behov?
Det nest siste skrittet er å lage grow_array. Som du antydet, bruker
man realloc til formålet:
void*
grow_array( char **old, size_t new_size )
{
void *tmp = realloc( old, new_size * sizeof *old );
/* error checking */
return tmp;
}
Det aller siste skrittet er å innse begrensninger av løsningen:
* den takler dårlig filer som inneholder '\0'
* den bruker kanskje relativt mye minne (fordobling av størrelsen er
en vanlig "vokseteknikk", men det har sine ulemper)
* strtok har sine særegenheter som man er nødt til å være klar over
* Noe annet?
Som sagt, bare et eksempel.
ivr
[*] Variable Length Arrays. Dog, jeg har vansker med å se hvordan
VLA'ene skulle ha hjulpet her. Man kan selvsagt løpe gjennom dataene
to ganger -- en for å telle antall "linjer" du skal ha, og den andre
for å fikse pekere i parray, men det kan man like gjerne greie seg
uten VLA'ene
[**] cdecl er et fryktelig nyttig program. Det å dekode deklarasjonen
av signal på SYS V er _ikke_ enkelt den første gangen man ser den.
[***] Chris Torek skrev utallige ganger på njus hva en peker og en
array er. Ta en titt fx. her <URL:
http://67.40.109.61/torek/c/pa.html>
[****] Peter van der Linden har skrevet en av de bedre bøkene om C --
"Expert C Programming: Deep C Secrets". I tillegg til å ha valgt en svært
humoristisk fremstilling av de mindre finere deler av C, synes det at
mannen jobbet for SunSoft og han mobber Microsoft for alt han er god
for. Dette er en veldig underholdende og lærerik bok.
--
<peder> igorr: tcl ja... det er fra de dypeste avgrunnene i helvete det...
<peder> php er bare fra foajeen
-- pederst på irc