printf( "%-15s %d\n", tbl->name, tbl->value ); } } int main(){ char buf[80]; struct elem *ptr; printtable(table); for(;;){ printf( "-> " ); if( gets( buf ) == NULL) break; /* EOF */ if( ! strcmp( buf, "q" )) exit(0); /* quit: vyhod */ ptr = find( buf, table, SIZE-1 ); if( ptr ) printf( "%d\n", ptr->value ); else { printf( "--- Ne najdeno ---\n" ); printtable(table); } } return 0; } 7.26. Napishem funkciyu, kotoraya preobrazuet stroku tak, chto pri ee pechati bukvy v nej budut podcherknuty, a cifry - vydeleny zhirno. Format teksta s vydeleniyami, kotoryj sozdaetsya etim primerom, yavlyaetsya obshcheprinyatym v UNIX i raspoznaetsya nekotorymi prog- rammami: naprimer, programma prosmotra fajlov less (more) vydelyaet takie bukvy na ekrane special'nymi shriftami ili inversiej fona. #define LEN 9 /* potom napishite 256 */ char input[] = "(xxx+yyy)/123.75=?"; char output[LEN]; void main( void ){ int len=LEN, i; void bi_conv(); char c; bi_conv(input, output, &len); if(len > LEN){ printf("Uvelich' LEN do %d\n", len); len = LEN; /* dostupnyj maksimum */ } for(i=0; i < len && (c = output[i]); ++i) putchar(c); putchar('\n'); } /* Zamet'te, chto include-fajly ne obyazatel'no * dolzhny vklyuchat'sya v samom nachale programmy! */ #include <stdio.h> #include <ctype.h> #define PUT(c) { count++; \ if(put < *len){ *p++ = (c); ++put;}} #define GET() (*s ? *s++ : EOF) void bi_conv( A. Bogatyrev, 1992-95 - 290 - Si v UNIX /*IN*/ char *s, /*OUT*/ char *p, /*INOUT*/ int *len ){ int count, put, c; for(count=put=0; (c=GET()) != EOF; ){ /* zhirnyj: C\bC */ /* podcherknutyj: _\bC */ if(isalpha(c)){ PUT('_'); PUT('\b'); } else if(isdigit(c)){ PUT( c ); PUT('\b'); } PUT(c); } PUT('\0'); /* zakryt' stroku */ *len = count; #undef PUT #undef GET } Napishite programmu dlya podobnoj obrabotki fajla. Zametim, chto dlya etogo ne nuzhny promezhutochnye stroki input i output i postrochnoe chtenie fajla; vse, chto nado sdelat', eto opredelit' #define PUT(c) if(c)putchar(c) #define GET() getchar() Napishite podobnuyu funkciyu, udvaivayushchuyu bukvy v ssttrrookkee. 7.27. Napishite programmu, udalyayushchuyu iz fajla vydeleniya. Dlya etogo nado prosto uda- lyat' posledovatel'nosti vida C\b #include <stdio.h> #define NOPUT (-1) /* ne simvol ASCII */ /* Nazvaniya shriftov - v perechislimom tipe */ typedef enum { NORMAL=1, ITALICS, BOLD, RED=BOLD } font; int ontty; font textfont; /* tekushchee vydelenie */ #define setfont(f) textfont=(f) #define getfont() (textfont) #define SetTtyFont(f) if(ontty) tfont(f) /* Ustanovit' vydelenie na ekrane terminala */ void tfont(font f){ /* tol'ko dlya ANSI terminala */ static font ttyfont = NORMAL; if(ttyfont == f) return; printf("\033[0m"); /* set NORMAL font */ switch(ttyfont = f){ case NORMAL: /* uzhe sdelano vyshe */ break; case BOLD: printf("\033[1m"); break; case ITALICS: /* use reverse video */ printf("\033[7m"); break; } } void put(int c){ /* Vyvod simvola tekushchim cvetom */ if(c == NOPUT) return; /* '\b' */ SetTtyFont(getfont()); putchar(c); setfont(NORMAL); /* Ozhidat' novoj C\b posl-ti */ } void main(){ register int c, cprev = NOPUT; /* Standartnyj vyvod - eto terminal ? */ ontty = isatty(fileno(stdout)); setfont(NORMAL); while((c = getchar()) != EOF){ A. Bogatyrev, 1992-95 - 291 - Si v UNIX if(c == '\b'){ /* vydelenie */ if((c = getchar()) == EOF) break; if(c == cprev) setfont(BOLD); else if(cprev == '_') setfont(ITALICS); else /* nalozhenie A\bB */ setfont(RED); } else put(cprev); cprev = c; } put(cprev); /* poslednyaya bukva fajla */ SetTtyFont(NORMAL); } 7.28. Napishite programmu pechati na printere listinga Si-programm. Klyuchevye slova yazyka vydelyajte dvojnoj nadpechatkoj. Dlya vydachi na terminal napishite programmu, pod- cherkivayushchuyu klyuchevye slova (podcherkivanie - v sleduyushchej stroke). Uproshchenie: vyde- lyajte ne klyuchevye slova, a bol'shie bukvy. Ukazanie: dlya dvojnoj pechati ispol'zujte upravlyayushchij simvol '\r' - vozvrat k nachalu toj zhe stroki; zatem stroka pechataetsya povtorno, pri etom simvoly, kotorye ne dolzhny pechatat'sya zhirno, sleduet zamenit' na probely (ili na tabulyaciyu, esli etot simvol sam est' '\t'). 7.29. Napishite programmu, pechatayushchuyu teksty Si-programm na printere. Vydelyajte klyu- chevye slova yazyka zhirnym shriftom, stroki "stroka", simvoly 'c' i kommentarii - kursi- vom. SHrifty dlya EPSON-FX sovmestimyh printerov (naprimer EP-2424) pereklyuchayutsya takimi upravlyayushchimi posledovatel'nostyami (ESC oznachaet simvol '\033'): VKLYUCHENIE VYKLYUCHENIE zhirnyj shrift (bold) ESC G ESC H utolshchennyj shrift (emphasized) ESC E ESC F kursiv (italics) ESC 4 ESC 5 podcherkivanie (underline) ESC - 1 ESC - 0 povyshennoe kachestvo pechati ESC x 1 ESC x 0 (near letter quality) nlq draft verhnie indeksy (superscript) ESC S 0 ESC T nizhnie indeksy (subscript) ESC S 1 ESC T szhatyj shrift (17 bukv/dyujm) '\017' '\022' (condensed) dvojnaya shirina bukv ESC W 1 ESC W 0 (expanded) proporcional'naya pechat' ESC p 1 ESC p 0 (proportional spacing) Mozhno vklyuchit' odnovremenno neskol'ko iz perechislennyh vyshe rezhimov. V kazhdoj iz sleduyushchih dvuh grupp nado vybrat' odno iz treh: pitch (plotnost' pechati) pica (10 bukv/dyujm) ESC P elite (12 bukv/dyujm) ESC M micron (15 bukv/dyujm) ESC g font (shrift) chernovik (draft (Roman)) ESC k '\0' tekst (text (Sans Serif)) ESC k '\1' kur'er (courier) ESC k '\2' Vsyudu vyshe 0 oznachaet libo '0' libo '\0'; 1 oznachaet libo '1' libo '\1'. Primer: printf( "This is \033Gboldface\033H word\n"); A. Bogatyrev, 1992-95 - 292 - Si v UNIX 7.30. Sostav'te programmu vyvoda nabora fajlov na pechat', nachinayushchuyu kazhdyj ochered- noj fajl s novoj stranicy i pechatayushchuyu pered kazhdym fajlom zagolovok i nomer tekushchej stranicy. Ispol'zujte simvol '\f' (form feed) dlya perevoda lista printera. 7.31. Napishite programmu pechati teksta v dve kolonki. Ispol'zujte bufer dlya formi- rovaniya lista: fajl chitaetsya postrochno (slishkom dlinnye stroki obrubat'), snachala zapolnyaetsya levaya polovina lista (bufera), zatem pravaya. Kogda list polnost'yu zapol- nen ili fajl konchilsya - vydat' list postrochno, raspisat' bufer probelami (ochistit' list) i povtorit' zapolnenie ocherednogo lista. Ukazanie: razmery lista dolzhny pereda- vat'sya kak argumenty main(), dlya bufera ispol'zujte dvumernyj massiv bukv, pamyat' dlya nego zakazyvajte dinamicheski. Uslozhnenie: ne obrubajte, a perenosite slishkom dlinnye stroki (stroka mozhet potrebovat' dazhe perenosa s lista na list). /* PROGRAMMA PECHATI V DVE POLOSY: pr.c */ #include <stdio.h> #include <string.h> #define YES 1 #define NO 0 #define FORMFEED '\f' #define LINEFEED '\n' extern char *malloc(unsigned); extern char *strchr(char *, char); void untab(register char *s); void resetsheet( void ); void addsheet( char *s, FILE *fpout ); void flushsheet( FILE *fpout ); void printline( int y, char *s, char *attr, FILE *fpout ); void doattr( register char *abuf, register char *vbuf ); void printcopy( FILE *fpin, FILE *fpout ); void main(void); char *strdup (const char *s){ char *p = malloc(strlen(s)+1); strcpy(p,s); return p; /* return strcpy((char *) malloc(strlen(s)+1), s); */ } /* ... tekst funkcii untab() ... */ int Sline; /* stroka na liste */ int Shalf; /* polovina lista */ int npage; /* nomer stranicy */ int startpage = 1; /* pechat' nachinaya s 1oj stranicy */ int fline; /* nomer stroki fajla */ int topline = 0; /* smeshchenie do nachala lista */ int halfwidth; /* shirina polulista */ int twocolumns = YES; /* v dve kolonki ? */ int lshift, rshift = 1; /* polya sleva i sprava */ typedef unsigned short ushort; int COLS = 128; /* shirina lista (bukv) */ int LINES = 66; /* dlina lista (strok) */ ushort *mem; /* bufer lista */ #define AT(x,y) mem[ (x) + (y) * COLS ] /* Vydelit' bufer pod list i zachistit' ego */ void resetsheet ( void ){ register x; if( mem == NULL ){ /* vydelit' pamyat' */ A. Bogatyrev, 1992-95 - 293 - Si v UNIX if ((mem = (ushort *) malloc (COLS * LINES * sizeof(ushort))) == NULL ){ fprintf(stderr, "Out of memory.\n"); exit(1); } } /* ochistit' */ for( x= COLS * LINES - 1 ; x >= 0 ; x-- ) mem[x] = ' ' & 0xFF; halfwidth = (twocolumns ? COLS/2 : COLS ) - (lshift + rshift ); Sline = topline; Shalf = 0; } #define NEXT_HALF \ if( twocolumns == YES && Shalf == 0 ){ \ /* zakryt' dannuyu polovinu lista */ \ Shalf = 1; /* perejti k novoj polovine */ \ Sline = topline; \ } else \ flushsheet(fpout) /* napechatat' list */ /* Zapisat' stroku v list */ void addsheet ( char *s, FILE *fpout ) { register x, y; register i; char *rest = NULL; int wrap = NO; /* YES kogda idet perenos slishkom dlinnoj stroki */ /* v kakoe mesto pomestit' stroku? */ x = (Shalf == 0 ? 0 : COLS/2) + lshift; y = Sline; i = 0; /* poziciya v stroke s */ while (*s) { if( *s == '\f' ){ /* vynuzhdennyj form feed */ rest = strdup( s+1 ); /* ostatok stroki */ NEXT_HALF; if( *rest ) addsheet(rest, fpout); free( rest ); return; } if( i >= halfwidth ){ /* perenesti dlinnuyu stroku */ wrap = YES; rest = strdup(s); break; } /* Obrabotka vydelenij teksta */ if( s[1] == '\b' ){ while( s[1] == '\b' ){ AT(x, y) = (s[0] << 8) | (s[2] & 0xFF); /* overstrike */ s += 2; } s++; x++; i++; } else { AT (x, y) = *s++ & 0xFF; A. Bogatyrev, 1992-95 - 294 - Si v UNIX x++; i++; } } /* Uvelichit' stroku/polovinu_lista */ Sline++; if (Sline == LINES) { /* polulist zapolnen */ NEXT_HALF; } if( wrap && rest ) { /* dopisat' ostatok stroki */ addsheet(rest, fpout); free(rest); } } int again; /* nuzhna li povtornaya nadpechatka? */ /* Napechatat' zapolnennyj list */ void flushsheet ( FILE *fpout ){ register x, y, xlast; char *s, *p; static char outbuf[BUFSIZ], attr[BUFSIZ]; /* attr - bufer pod atributy vydelenij */ ushort c; if( npage >= startpage ) for (y = 0; y < LINES; y++) { /* obrezat' koncevye probely */ for (xlast = (-1), x = COLS - 1; x >= 0; x--) if (AT (x, y) != ' ') { xlast = x; break; } again = NO; s = outbuf; p = attr; for (x = 0; x <= xlast; x++){ c = AT(x, y); *s++ = c & 0xFF; /* imeet atributy ? */ c >>= 8; c &= 0xFF; *p++ = c ? c : ' '; if( c ) again = YES; } *s = '\0'; *p = '\0'; printline(y, outbuf, attr, fpout); } npage++; /* next page */ resetsheet(); /* zachistit' novyj list */ } /* Napechatat' odnu stroku lista */ void printline ( int y, char *s, char *attr, FILE *fpout ){ register x; if( again ){ doattr(attr, s); fprintf(fpout, "%s\r", attr ); } fprintf(fpout, "%s", s); /* perevod lista ili stroki */ fputc( y == LINES-1 ? FORMFEED : LINEFEED, fpout ); } /* Proverit' - net li atributov vydelenij */ void doattr ( register char *abuf, register char *vbuf ){ for(; *abuf; abuf++, vbuf++ ) if( !strchr(" _-!|\177", *abuf)) *abuf = *vbuf; } A. Bogatyrev, 1992-95 - 295 - Si v UNIX /* Kopirovanie fajla na printer */ void printcopy ( FILE *fpin, FILE *fpout ) { char inbuf[BUFSIZ]; npage = 1; /* pervaya stranica imeet nomer 1 */ fline = 0; /* tekushchaya stroka fajla - 0 */ resetsheet(); /* zachistit' bufer lista */ while( fgets(inbuf, sizeof inbuf - 1, fpin ) != NULL ){ register l = strlen( inbuf ); if( l && inbuf[l-1] == '\n' ) inbuf[--l] = '\0' ; fline++; untab ( inbuf ); addsheet( inbuf, fpout ); } if( !(Sline == topline && Shalf == 0)) /* esli stranica ne byla tol'ko chto zachishchena ... */ flushsheet(fpout); fprintf(stderr, "%d strok, %d listov.\n", fline, npage-1); } /* Vyzov: pr < fajl > /dev/lp */ void main (){ printcopy(stdin, stdout); } Fajl-printer imeet v UNIX imya /dev/lp ili podobnoe emu, a v MS DOS - imya prn. 7.32. Napishite programmu, kotoraya postrochno schityvaet nebol'shoj fajl v pamyat' i pechataet stroki v obratnom poryadke. Ukazanie: ispol'zujte dinamicheskuyu pamyat' - funkcii malloc() i strcpy(). Ob®yasnim, pochemu zhelatel'no pol'zovat'sya dinamicheskoj pamyat'yu. Pust' my znaem, chto stroki imeyut maksimal'nuyu dlinu 80 simvolov i maksimal'noe kolichestvo strok ravno 50. My mogli by hranit' tekst v dvumernom massive: char text[50][80]; zanimayushchem 50*80 = 4000 bajt pamyati. Pust' teper' okazalos', chto stroki fajla v dejstvitel'nosti imeyut dlinu po 10 bukv. My ispol'zuem 50 * (10 + 1) = 550 bajt ne ispol'zuem 4000 - 50 * (10 + 1) = 3450 bajt (+1 nuzhen dlya simvola '\0' na konce stroki). Pust' my teper' pishem char *text[50]; int i=0; i pri chtenii ocherednoj stroki sohranyaem ee tak: char buffer[81], *malloc(), *gets(); while( gets(buffer) != NULL ){ text[i] = (char *) malloc(strlen(buffer)+1); /* +1 dlya hraneniya \0, kotoryj ne uchten strlen-om */ strcpy(text[i++], buffer); } to est' zakazyvaem rovno stol'ko pamyati, skol'ko nado dlya hraneniya stroki i ni bajtom bol'she. Zdes' my (esli sizeof(char *)==4) ispol'zuem A. Bogatyrev, 1992-95 - 296 - Si v UNIX 50 * 4 + 50 * (10 + 1 + 4) = 950 bajt massiv ukazatelej + zakazannaya malloc pamyat' (+4 - sluzhebnaya informaciya malloc), no zato u nas ne ostaetsya neispol'zuemoj pamyati. Preimushchestvom vydeleniya pamyati v vide massiva yavlyaetsya to, chto eta pamyat' vydelitsya GARANTIROVANNO, togda kak malloc()-u mozhet ne hvatit' pamyati (esli my ee prezhde ochen' mnogo zahvatyvali i ne osvobozhdali free()). Esli malloc ne mozhet vydelit' uchastok pamyati trebuemogo razmera, on vozvrashchaet znachenie NULL: if((text[i] = malloc(....)) == NULL) { fprintf(stderr, "Malo pamyati\n"); break; } Raspechatka strok: for(--i; i >= 0; i-- ){ printf("%s\n", text[i]); free( text[i] ); } Funkciya free(ptr) "osvobozhdaet"|- otvedennuyu ranee malloc()om ili calloc()om oblast' pamyati po adresu ptr tak, chto pri novyh vyzovah malloc() eta oblast' mozhet byt' pere- ispol'zovana. Dannye v osvobozhdennoj pamyati PORTYATSYA posle free(). Oshibochno (i opasno) osvobozhdat' pamyat', kotoraya NE BYLA otvedena malloc()-om! Organizaciya teksta v vide massiva ssylok na stroki ili spiska ssylok na stroki, a ne v vide dvumernogo tekstovogo polya, vygodna eshche tem, chto takie stroki proshche perestavlyat', sortirovat', vstavlyat' stroku v tekst, udalyat' stroku iz teksta. Pri etom perestavlyayutsya lish' ukazateli v linejnom massive, a sami stroki nikuda ne kopi- ruyutsya. V dvumernom zhe bajtovom massive nam prishlos' by dlya teh zhe perestanovok kopirovat' celye massivy bajt - stroki etoj tekstovoj matricy. 7.33. Napishite programmu, pechatayushchuyu stroki fajla v obratnom poryadke. Ne schityvat' fajl celikom v pamyat'! Sleduet ispol'zovat' metod "obratnogo chteniya" libo metod "bystrogo dostupa" k strokam fajla, opisannyj v glave "Rabota s fajlami". ____________________ |- Na samom dele vse osvobozhdennye kuski vklyuchayutsya v spisok svobodnoj pamyati, i skleivayutsya vmeste, esli dva osvobozhdennyh kuska okazalis' ryadom. Pri novyh vyzovah malloc snachala prosmatrivaetsya spisok svobodnoj pamyati - net li tam oblasti dostatoch- nogo razmera? |tot algoritm opisan u Kernigana i Ritchi. A. Bogatyrev, 1992-95 - 297 - Si v UNIX /* Invertirovanie poryadka strok v fajle. * Ispol'zuetsya ta ideya, chto fajl-rezul'tat imeet tot zhe * razmer, chto i ishodnyj */ #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #define BUFS 4096 /* maksimal'naya dlina stroki */ void main(int argc, char **argv ) { FILE *fp; struct stat st; long len; char buffer[ BUFS+1 ]; FILE *fpnew; /* inversnyj fajl */ int lgt; if( argc != 2 ){ printf("Error: must be filename\n"); exit(1); } if( (fp= fopen( argv[1], "r" )) == NULL ){ printf( "Can not open %s\n", argv[1] ); exit(2); } stat( argv[1], &st ); /* fstat(fileno(fp), &st); */ len = st.st_size; /* dlina fajla v bajtah */ if( (fpnew = fopen( "inv.out", "w" ))== NULL ){ printf("Can not create file\n"); exit(3); } while( fgets( buffer, sizeof buffer, fp ) != NULL ){ lgt = strlen( buffer ); fseek(fpnew, len - lgt , 0); /* Pomnite, chto smeshchenie u lseek i fseek - * eto chislo tipa long, a ne int. * Poetomu luchshe vsegda pisat' * lseek(fd, (long) off, whence); */ len -= lgt; fprintf( fpnew, "%s", buffer ); /* ili luchshe fputs(buffer, fpnew); */ } fclose( fp ); fclose( fpnew ); } 7.34. Napishite programmu, kotoraya chitaet fajl, sostoyashchij iz "blokov" teksta, razde- lennyh pustymi strokami. Razmer "bloka" ogranichen. Programma gotovit fajl dlya pechati na printer tak, chtoby ni odin blok ne razbivalsya na chasti: A. Bogatyrev, 1992-95 - 298 - Si v UNIX ----------- ----------- |###### A | |###### A | list1 |#### A | prevrashchat' |#### A | |##### A | v |##### A | | | | | |###### B | | | ----------- ----------- |#### B | |###### B | list2 | | |#### B | ... | | to est' esli blok ne umeshchaetsya na ostatke lista, on dolzhen byt' perenesen na sleduyu- shchij list. Bloki sleduet razdelyat' odnoj pustoj strokoj (no pervaya stroka lista ne dolzhna byt' pustoj!). Esli blok dlinnee stranicy - ne perenosite ego. /* Reshenie zadachi o perenose blokov teksta, * esli oni ne umeshchayutsya na ostatke lista */ #include <stdio.h> #include <ctype.h> extern void *malloc(unsigned); extern int atoi(char *); FILE *fpin = stdin, *fpout = stdout; /* Spasti stroku v dinamicheski vydelennoj pamyati */ char *strdup (const char *s) { char *ptr = (char *) malloc (strlen (s) + 1); if( ptr ) strcpy (ptr, s); return ptr; } int page_length = 66; /* dlina stranicy */ int current_line; /* tekushchaya stroka na stranice (s nulya) */ int numbered = 0; /* numerovat' stroki lista ? */ #define MAXLINES 256 /* maks. dlina bloka */ int stored = 0; /* zapomneno strok */ char *lines[MAXLINES]; /* zapomnennye stroki */ /* Zapomnit' stroku bloka v bufer strok */ void remember (char *s) { if (stored >= MAXLINES) { fprintf (stderr, "Slishkom dlinnyj blok.\n"); return; } else if((lines[stored++] = strdup (s)) == NULL ){ fprintf (stderr, "Malo pamyati (Out of memory).\n"); exit(13); } } /* Perehod na sleduyushchuyu stranicu */ void newpage () { current_line = 0; putc('\f', fpout); } A. Bogatyrev, 1992-95 - 299 - Si v UNIX /* Perevod stroki ili lista */ void newline (void) { if (current_line == page_length - 1) newpage (); /* nachat' novyj list */ else { current_line++; if( numbered ) fprintf(fpout, "%02d\n", current_line); else putc ('\n', fpout); } } /* Perehod na sleduyushchuyu stranicu vstavkoj pustyh strok */ void nextpage () { while (current_line != 0) newline (); } /* Vydat' spasennyj blok */ void throwout () { register i; for (i = 0; i < stored; i++) { if( numbered ) fprintf(fpout, "%02d %s", current_line, lines[i]); else fputs (lines[i], fpout); newline (); free (lines[i]); } stored = 0; } /* Vydat' blok, perenosya na sleduyushchij list esli nado */ void flush () { int rest_of_page = page_length - current_line; /* ostalos' pustyh strok na stranice */ if ((stored > page_length && rest_of_page < page_length / 4) || rest_of_page < stored) nextpage (); throwout (); if (current_line) /* ne pervaya stroka lista */ newline (); /* razdelitel' blokov */ } /* Obrabotat' vhodnoj fajl */ void process () { char buffer[512]; int l; while (fgets (buffer, sizeof buffer, fpin) != NULL) { if ((l = strlen (buffer)) && buffer[l - 1] == '\n') buffer[ --l] = '\0'; if (l) remember (buffer); /* a po pustoj stroke - vydat' blok */ else if (stored) flush (); } if (stored) flush (); nextpage(); } A. Bogatyrev, 1992-95 - 300 - Si v UNIX void main (int argc, char *argv[]) { argc--; argv++; while (*argv) { if (**argv == '-') { char *key = *argv + 1, *arg; switch (*key) { case 'l': if (! key[1]) { if( argv[1] ){ arg = argv[1]; argv++; argc--; } else arg = ""; } else arg = key+1; if( isdigit(*arg) ){ page_length = atoi(arg); fprintf (stderr, "Dlina stranicy: %d strok\n", page_length); } else fprintf(stderr, "-l CHISLO\n"); break; case 'n': numbered++; break; default: fprintf (stderr, "Neizvestnyj klyuch %s\n", key); break; } } argv++; argc--; } process (); exit(0); } 7.35. Sostav'te programmu vyvoda strok fajla v inversnom otobrazhenii, prichem poryadok simvolov v strokah takzhe sleduet invertirovat'. Naprimer, abcdef ... oklmn 987654321 ..... prevrashchat' v ..... 123456789 nmlko ... fedcba Programma dolzhna byt' sostavlena dvumya sposobami: pri pomoshchi obratnogo chteniya fajla i rekursivnym vyzovom samoj funkcii invertirovaniya. Ukazanie: pri obratnom chtenii nado chitat' fajl bol'shimi kuskami (blokami). 7.36. Napishite programmu, chitayushchuyu fajl postrochno i razmeshchayushchuyu stroki v otsortiro- vannoe dvoichnoe derevo. Po koncu fajla - raspechatajte eto derevo. Ukazanie: ispol'- zujte dinamicheskuyu pamyat' i rekursiyu. A. Bogatyrev, 1992-95 - 301 - Si v UNIX /* Dvoichnaya sortirovka strok pri pomoshchi dereva */ #include <stdio.h> char buf[240]; /* bufer vvoda */ int lines; /* nomer stroki fajla */ typedef struct node{ struct _data{ /* DANNYE */ char *key; /* klyuch - stroka */ int line; /* nomer stroki */ } data; /* SLUZHEBNAYA INFORMACIYA */ struct node *l, /* levoe podderevo */ *r; /* pravoe podderevo */ } Node; Node *root = NULL; /* koren' dereva (ssylka na verhnij uzel) */ /* Otvedenie pamyati i inicializaciya novogo uzla */ Node *newNode(s) char *s; /* stroka */ { Node *tmp; extern char *malloc(); /* vydelitel' pamyati */ tmp = (Node *) malloc(sizeof(Node)); if( tmp == NULL ){ fprintf( stderr, "Net pamyati.\n"); exit(1); } tmp -> l = tmp -> r = NULL; /* net podderev'ev */ tmp -> data.line = lines; /* nomer stroki fajla */ tmp -> data.key = malloc( strlen(s) + 1 ); /* +1 - pod bajt '\0' v konce stroki */ strcpy(tmp -> data.key, s); /* kopiruem klyuch v uzel */ return tmp; } int i; /* Vyneseno v staticheskuyu pamyat', chtoby pri kazhdom * rekursivnom vyzove ne sozdavalas' novaya auto-peremennaya, * a ispol'zovalas' odna i ta zhe staticheskaya */ A. Bogatyrev, 1992-95 - 302 - Si v UNIX /* Rekursivnaya pechat' dereva */ void printtree(root, tree, level, c) Node *root; /* koren' dereva */ Node *tree; /* derevo */ int level; /* uroven' */ char c; /* imya poddereva */ { if( root == NULL ){ printf("Derevo pusto.\n"); return; } if( tree == NULL ) return; /* esli est' - raspechatat' levoe podderevo */ printtree (root, tree -> l, level + 1, '/'); /* 'L' */ /* raspechatat' klyuch uzla */ for( i=0; i < level; i++ ) printf(" "); printf("%c%3d--\"%s\"\n", c, tree-> data.line, tree -> data.key); /* esli est' - raspechatat' pravoe podderevo */ printtree(root, tree -> r, level + 1, '\\'); /* 'R' */ } void prTree(tree) Node *tree; { printtree(tree, tree, 0, '*'); } /* Dobavit' uzel s klyuchom key v derevo tree */ void addnode(tree, key) Node **tree; /* v kakoe derevo dobavlyat': adres peremennoj, * soderzhashchej ssylku na kornevoj uzel */ char *key; /* klyuch uzla */ { #define TREE (*tree) if( TREE == NULL ){ /* derevo poka pusto */ TREE = newNode( key ); return; } /* inache est' hot' odin uzel */ if ( strcmp (key, TREE -> data.key) < 0 ) { /* dobavit' v levoe podderevo */ if ( TREE -> l == NULL ){ /* net levogo dereva */ TREE -> l = newNode(key); return; } else addnode( & TREE ->l , key); } A. Bogatyrev, 1992-95 - 303 - Si v UNIX else{ /* dobavit' v pravoe derevo */ if ( TREE -> r == NULL ){ /* net pravogo poddereva */ TREE -> r = newNode(key); return; } else addnode ( & TREE ->r, key) ; } } /* Procedura udaleniya iz dereva po klyuchu. */ typedef struct node *NodePtr; static NodePtr delNode; /* udalyaemaya vershina */ void delete(key, tree) char *key; /* klyuch udalyaemogo elementa */ NodePtr *tree; /* iz kakogo dereva udalyat' */ { extern void doDelete(); if(*tree == NULL){ printf( "%s ne najdeno\n", key ); return; } /* poisk klyucha */ else if(strcmp(key, (*tree)->data.key) < 0) delete( key, &(*tree)->l ); else if(strcmp(key, (*tree)->data.key) > 0) delete( key, &(*tree)->r ); else{ /* klyuch najden */ delNode = *tree; /* ukazatel' na udalyaemyj uzel */ if(delNode->r == NULL) *tree = delNode->l; else if(delNode->l == NULL) *tree = delNode->r; else doDelete( & delNode->l ); free(delNode); } } static void doDelete(rt) NodePtr *rt; { if( (*rt)->r != NULL ) /* spusk po pravoj vetvi */ doDelete( &(*rt)->r ); else{ /* perenos dannyh v drugoj uzel */ delNode->data = (*rt)->data; delNode = *rt; /* dlya free() */ *rt = (*rt)->l; } } A. Bogatyrev, 1992-95 - 304 - Si v UNIX void main(){ extern char *gets(); char *s; while (gets(buf) != NULL){ /* poka ne konec fajla */ lines++; addnode( & root, buf ); } prTree(root); /* udalim stroku */ freopen("/dev/tty", "r", stdin); do{ printf( "chto udalit' ? " ); if((s = gets(buf)) == NULL) break; delete(buf, &root); prTree( root ); } while( s && root ); printf("Bye-bye.\n"); exit(0); } 7.37. Napishite programmu, kotoraya chitaet so standartnogo vvoda 10 chisel libo slov, a zatem raspechatyvaet ih. Dlya hraneniya vvedennyh dannyh ispol'zujte ob®edinenie. #include <stdio.h> #include <ctype.h> #define INT 'i' #define STR 's' struct data { char tag; /* teg, pometka. Kod tipa dannyh. */ union { int i; char *s; } value; } a[10]; int counter = 0; /* schetchik */ void main(){ char word[128]; int i; char *malloc(unsigned); /* CHtenie: */ for(counter=0; counter < 10; counter++){ if( gets(word) == NULL ) break; if( isdigit((unsigned char) *word)){ a[counter].value.i = atoi(word); a[counter].tag = INT; } else { a[counter].value.s = malloc(strlen(word)+1); strcpy(a[counter].value.s, word); a[counter].tag = STR; } } /* Raspechatka: */ for(i=0; i < counter; i++) switch(a[i].tag){ case INT: printf("chislo %d\n", a[i].value.i); break; case STR: printf("slovo %s\n", a[i].value.s); free(a[i].value.s); break; } A. Bogatyrev, 1992-95 - 305 - Si v UNIX } 7.38. Rassmotrim zadachu napisaniya funkcii, kotoraya obrabatyvaet peremennoe chislo argumentov, naprimer funkciyu-generator menyu. V takuyu funkciyu nado podavat' stroki menyu i adresa funkcij, vyzyvaemyh pri vybore kazhdoj iz strok. Sobstvenno problema, kotoruyu my tut obsuzhdaem - kak peredavat' peremennoe chislo argumentov v podobnye funkcii? My privedem tri programmy ispol'zuyushchie tri razlichnyh podhoda. Predpochtenie ne otdano ni odnomu iz nih - kazhdyj iz nih mozhet okazat'sya effektivnee drugih v opre- delennyh situaciyah. Dumajte sami! 7.38.1. Massiv /* Peredacha argumentov v funkciyu kak MASSIVA. * Sleduet yavno ukazat' chislo argumentov v massive. */ #include <stdio.h> /* printf(), NULL */ #include <string.h> /* strdup() */ #include <stdlib.h> /* malloc() */ #define A_INT 1 #define A_STR 2 #define A_NULL 0 typedef struct arg { int type; union jack { char *s; int d; } data; struct arg *next; } Arg; void doit(Arg args[], int n){ int i; for(i=0; i < n; i++) switch(args[i].type){ case A_INT: printf("%d", args[i].data.d); break; case A_STR: printf("%s", args[i].data.s); break; default: fprintf(stderr, "Unknown type!\n"); break; } } A. Bogatyrev, 1992-95 - 306 - Si v UNIX /* Pri inicializacii union nado ispol'zovat' tip * pervogo iz perechislennyh znachenij. */ Arg sample[] = { { A_INT, (char *) 123 }, { A_STR, (char *) " hello, " }, { A_INT, (char *) 456 }, { A_STR, (char *) " world\n" } }; int main(int ac, char *av[]){ doit(sample, sizeof sample / sizeof sample[0]); return 0; } 7.38.2. Spisok /* Peredacha argumentov v funkciyu kak SPISKA. * Dostoinstvo: spisok mozhno modificirovat' * vo vremya vypolneniya programmy: dobavlyat' i * udalyat' elementy. Nedostatok tot zhe: spisok nado * postroit' dinamicheski vo vremya vypolneniya, * zaranee etogo sdelat' nel'zya. * Nedostatkom dannoj programmy yavlyaetsya takzhe to, * chto spisok ne unichtozhaetsya posle ispol'zovaniya. * V C++ eta problema reshaetsya pri pomoshchi ispol'zovaniya * avtomaticheski vyzyvaemyh destruktorov. */ #include <stdio.h> /* printf(), NULL */ #include <string.h> /* strdup() */ #include <stdlib.h> /* malloc() */ #define A_INT 1 #define A_STR 2 #define A_NULL 0 typedef struct arg { int type; union jack { char *s; int d; } data; struct arg *next; } Arg; A. Bogatyrev, 1992-95 - 307 - Si v UNIX void doit(Arg *arglist){ for( ; arglist; arglist=arglist->next) switch(arglist->type){ case A_INT: printf("%d", arglist->data.d); break; case A_STR: printf("%s", arglist->data.s); break; default: fprintf(stderr, "Unknown type!\n"); break; } } Arg *new_int(int n, Arg *next){ Arg *ptr = (Arg *) malloc(sizeof(Arg)); ptr->type = A_INT; ptr->data.d = n; ptr->next = next; return ptr; } Arg *new_str(char *s, Arg *next){ Arg *ptr = (Arg *) malloc(sizeof(Arg)); ptr->type = A_STR; ptr->data.s = strdup(s); ptr->next = next; return ptr; } int main(int ac, char *av[]){ doit( new_int(123, new_str(" hello, ", new_int(456, new_str(" world\n", NULL)))) ); return 0; } 7.38.3. Funkciya s peremennym chislom parametrov /* Peredacha argumentov v funkciyu kak SPISKA ARGUMENTOV * FUNKCII s priznakom konca spiska. */ #include <stdio.h> /* printf(), NULL */ #include <stdarg.h> /* va_... */ #define A_INT 1 #define A_STR 2 #define A_NULL 0 A. Bogatyrev, 1992-95 - 308 - Si v UNIX void doit(...){ /* peremennoe chislo argumentov */ va_list args; /* vtoroj parametr - argument, predshestvuyushchij ... * Esli takogo net - stavim zapyatuyu i pustoe mesto! */ va_start(args, ); for(;;){ switch(va_arg(args, int)){ case A_INT: printf("%d", va_arg(args, int)); break; case A_STR: printf("%s", va_arg(args, char *)); break; case A_NULL: goto breakloop; default: fprintf(stderr, "Unknown type!\n"); break; } } breakloop: va_end(args); } int main(int ac, char *av[]){