Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

osn_progr_final

.pdf
Скачиваний:
37
Добавлен:
12.02.2016
Размер:
3.27 Mб
Скачать

printf(“номер вісімковий:”); printf(“%o\n”,c-‘0’); printf(“номер шістнадцятковий:”); printf(“%x\n”,c-‘0’); printf(“код десятковий:”); printf(“%d\n”,c);

printf(“код вісімковий:”); printf(“%o\n”,c);

printf(“код шістнадцятковий:”); printf(“%x\n”,c);

}

Приклад 2 Підрахувати кількість символів вхідного потоку

Розглянемо наступну програму:

#include <stdio.h> main()

{

long nc; nc = 0;

while (getchar() != EOF) ++nc;

printf("%1d\n", nc);

}

Програма підрахунку символів накопичує їхню кількість у змінну типу long, а не int (максимальне число32767), і якщо описати лічильник як int, те він буде переповнятися навіть при порівняно малому файлі . Специфікація перетворення %1d указує printf, що відповідний аргумент є цілим типу long .

Щоб справитися ще з більшими числами, ви можете використовувати тип double . Ми також використовуємо оператор for замість while для того, щоб проілюструвати інший спосіб запису циклу.

main()

{ /* count characters in * input */

double nc;

for (nc = 0; getchar() != EOF; ++nc); printf("%.0f\n", nc);

}

191

Функція printf використовує специфікацію %f як для float, так і для double; специфікація %.0f дозволяє не друкувати дробову частину. Тіло оператора циклу for тут порожнє, тому що вся робота виконується в перевірочній і ініціалізуючій частинах. Відзначимо, що якщо файл вводу не містить ніяких символів, то умова в while чи for не виконається при найпершому звертанні до getchar, і, отже, програма видасть нуль.

Приклад 3 Підрахувати кількість рядків у файлі .

Будемо вважати, що рядок закінчується \n.

main()

 

{

/* count lines in input */

int s, nl; nl = 0;

while ((s= getchar()) != EOF) if (s== '\n')

++nl; printf("%d\n", nl);

}

Тіло while тепер містить оператор if, що у свою чергу, керує оператором інкремента ++nl. Оператор if перевіряє вкладену в круглі дужки умову і, якщо вона істинна, виконує наступний за ним оператор

Приклад 4 Написати програму, що копіює символи вхідного файлу у вихідний

Загальна схема програми має вигляд:

ввести символ

while (символ не є ознакою кінця файлу) вивести тільки що прочитаний символ ввести новий символ

Ось текст програми:

#include <stdio.h>

 

main()

 

{

/* copy input tо output;

192

* 1st version */

int c;

c = getchar(); while (c != EOF) {

putchar(c);

c = getchar();

}

}

Основна проблема полягає в тому, щоб зафіксувати кінець файлу . Звичайно, коли функція getchar досягає кінця файлу, вона повертає значення, що не є дійсним символом. Але яке значення є ознакою кінця файлу? (Це може бути –1 чи 0). Щоб універсалізувати підхід в таких випадках використовується символічне ім”я (макровизначення) EOF, яке визначається як

#define EOF -1

або

#define EOF 0

В прикладі також описана змінна 'с' як int, а не char, для того щоб вона могла зберігати значення, що повертається getchar .

Ця програма, звичайно, могла б бути написана більш стисло. У мові C будь-яке присвоювання, як наприклад

c = getchar();

може бути використане у виразі; його значенням є значення, що присвоюється лівій частині. Тоді програма копіювання файлу запишеться у вигляді:

main()

{ /* copy input -> output; * 2nd version */

int c;

while ((c = getchar()) != EOF) putchar(c);

}

Програма отримує символ з стандартнго вводу, присвоює його змінній 'с' і потім перевіряє, чи не є цей символ ознакою кінця файлу. Якщо не є - виконується тіло оператора while, що виводить цей символ. Потім цикл while повторюється. Коли, нарешті, буде досягнутий кінець файлу введення, оператор while завершується, а разом з ним закінчується виконання і функції main.

193

Важливо зрозуміти, що круглі дужки навколо присвоювання в умовному виразі дійсно необхідні. Пріоритет операції != вищий, ніж операції присвоювання =, а це означає, що під час відсутності круглих дужок перевірка умови != буде виконана до присвоювання =. Таким чином, оператор

c = getchar() != EOF

еквівалентний оператору c = (getchar() != EOF)

Приклад 5 Напишіть програму з використанням циклів, котра малює трикутник:

*

***

*****

*******

*********

/* Трикутник із зірочок */

#include <stdio.h>

/* Друк n символів c */ printn(c, n){

while( --n >= 0 ) putchar(c);

}

int lines = 10; /* кількість рядків трикутника */ void main(argc, argv)

char *argv[];

{

register int nline; /* номер рядка */

register int naster; /* кількість зірочок в рядку */ register int i;

if( argc > 1 )

lines = atoi( argv[1] );

for( nline=0; nline < lines ; nline++ ){ naster = 1 + 2 * nline;

/* лідируючі пропуски */ printn(' ', lines-1 - nline);

/* зірочки */ printn('*', naster);

/* перевід рядка */ putchar( '\n' );

}

 

exit(0);

/* завершення програми */

194

}

Приклад 6 Написати програму, що підраховує кількість слів у файлі

Розглянемо програму, що підраховує кількість рядків, слів і символів. Будемо вважати, що словом є будь-яка послідовність символів, яка не містить пропусків чи символів табуляції.

#define yes 1 #define no 0

main()

{

int c, nl, nw, inword;

inword = no;

nl = nw = nc = 0;

while ((c = getchar()) != EOF) { ++nc;

if (з== '\n') ++nl;

if (c == ' ' || c == '\n' || c == '\t') inword = no;

else if (inword == no) { inword = yes; ++nw;

}

}

printf("%d %d %d\n", nl, nw, nc);

}

Щоразу, коли програма зустрічає перший символ слова, вона збільшує лічильник числа слів на одиницю. Змінна inword стежить за тим, чи знаходиться програма в даний момент всередині слова чи ні; спочатку цій змінній присвоюється " не в слові", чому відповідає значення no. Ми віддаємо перевагу символічним константам yes і no перед літерними значенням 1 і 0, тому що вони роблять програму більш зручною для читання. Також істотні зміни набагато легше вносити в ті програми, де числа фігурують тільки як константи.

Рядок

nl = nw = nc = 0;

195

дозволяє всім трьом змінним присвоїти нулі. операція || означає ”або”, так що рядок

if (c == ' ' || c == '\n' || c == '\t')

означає "якщо c - пропуск, чи c - символ нового рядка, чи c -табуляція

...". (Умовна послідовність \t є зображенням символу табуляції). Оператор, що слідує за else, є опертором if, що керує двома операторами у фігурних дужках.

Приклад 7 Підрахувати кількість входжень кожної з літер алфавіта в файл

#include <stdio.h> #include <ctype.h>

long bcnt[8];

 

 

 

char masks[8] = {

/* маски битов */

1, 2, 4, 8, 16, 32, 64, 128 };

 

long cnt[256];

/* счетчики для каждой из 256

букв */

 

 

 

/* роздруковка лытер */

 

 

 

char *pr( c ){

 

 

 

static char buf[ 20 ];

 

 

switch( c ){

 

 

 

case '\n': return

"

\\n "

;

case '\r': return

"

\\r "

;

case '\t': return

"

\\t "

;

case '\b': return

"

\\b "

;

case '\f': return

"

\\f "

;

case '\033': return "

ESC"

;

case '\0': return

"

\\0 "

;

case 0177: return

" ^? "

;

}

 

 

 

if( c < ' ' ){

sprintf( buf, " ^%c ", c + 'A' - 1 ); }else if( isspace(c)){

sprintf( buf, " '%c'", c ); }else if( ! isprint( c ))

sprintf( buf, "\\%3o", c ); else sprintf( buf, " %c ", c ); return buf;

}

196

main( argc, argv ) char **argv; { FILE *fp;

if( argc == 1 ) process( stdin ); else{ argv++; argc--;

while( *argv ){

printf( "----- FILE %s -----\n", *argv ); if((fp = fopen( *argv, "r" )) == NULL ){

printf( "Can not open\n" );

}else{ process( fp ); fclose( fp ); } argv++; argc--;

}

}

exit(0);

}

/* обробити файл з вказівником fp */ process( fp ) FILE *fp;

{register i; int c; int n;

/* зачистка лічильників */

for( i=0; i < 256; i++ ) cnt[i] = 0L; for( i=0; i < 8 ; i++ ) bcnt[i] = 0;

while( ( c=getc(fp)) != EOF ){ c &= 0377;

/* по букву */ cnt[ c ] ++;

/* підрахунок бітів */ for( i=0; i < 8; i++ )

if( c & masks[i] ) bcnt[ i ] ++;

}

/* видача результатів в COL колонок */

#define COL 4

printf( "\tASCII map\n" ); for( n=i=0; i < 256; i++ ){

/* if( cnt[i] == 0l ) continue; */

printf( "%s %5ld |", pr(i), cnt[i] );

if( ++n == COL ){ n = 0; putchar('\n'); }

/* чи if((i % COL) == (COL-1)) putchar('\n'); */

}

printf( "\n\tBITS map\n" );

197

for( i=7; i >=0 ; i-- ) printf( "%6d ", i ); putchar( '\n' );

for( i=7; i >=0 ; i-- )

printf( "%6ld ", bcnt[i] ); putchar( '\n' ); putchar( '\n' );

}

Приклад 8 Напишемо програму, що зчитує рядки і друкує найдовший з них.

Схема програми наступна: while (є ще рядок)

if (цей рядок довший самого довшого з попередніх) запам'ятати цей рядок і його довжину надрукувати самий довгий рядок

За цією схемою ясно, що програма природньо розпадається на кілька частин. Одна частина читає новий рядок, інша перевіряє його, третя запам'ятовує, а інші частини програми керують цим процесом.

#define maxline 1000 /* довжина максимального рядка*/ main()

{int len; /* довжина біжучого рядка */

int

max; /* максимальна довжина */

char

line[maxline]; /* біжучий рядок */

char

save[maxline];/*найдовший збережений рядок */

max = 0;

while ((len = getline(line, maxline)) > 0) if (len > max) {

max = len; copy(line, save);

}

if (max > 0) printf("%s", save);

}

getline(s, lim) /* отримує рядок та записує його в * s,повертає довжину рядка */

char s[]; int lim;

{

int c, i; for (i = 0;

i < lim - 1 && (c = getchar()) != EOF && c != '\n';

198

++i)

s[i] = c; if (c == '\n') {

s[i] = c; ++i;

}

s[i] = '\0'; return (i);

}

 

 

copy(s1, s2)

/* копіює s1 в s2; */

char

s1[], s2[];

 

{

 

 

int

i;

 

i = 0;

while ((s2[i] = s1[i]) != '\0') ++i;

}

Функції main і getline “спілкуються” як через пару аргументів, так і через значення, що повертається. Аргументи getline описані в рядках

char

s[];

int

lim;

які вказують, що перший аргумент є масивом, а другий - цілим. Довжина масиву s не зазначена, тому що вона визначена в main . Функція getline використовує оператор return для передачі значення назад, точно так само, як це робила функція power . Щоб позначити кінець рядка символів, функція getline поміщає в кінець створюваного нею масиву символ ‘\0’.

Специфікація формату %s вказує, що printf очікує рядок, що закінчується нуль-символом. Проаналізувавши функцію copy, бачимо , що і її вхідний аргумент повинен закінчуватись символом ‘\0’, вона копіює цей символ у вихідний аргумент s2.

Відмітимо, що функція getline при заповненні масиву припиняє подальше отримання символів, якщо рядок перевищив максимальнодопустиму довжину, навіть якщо не зустрічає символу нового рядка. Перевіривши отриману довжину й останній символ, функція main може встановити, чи не був цей рядок занадто довгим.

Користувач функції getline ніяк не може заздалегідь довідатися, наскільки довгим виявиться рядок, що вводиться. Тому в getline включений контроль переповнення. У той же час користувач функції copy вже знає /чи може довідатися/, який розмір рядків, тому додаткового контролю не потрібно.

199

Приклад 9 Написати програму, що друкує слова в рядках файла в зворотному порядку

#include <stdio.h> #include <ctype.h> #include <string.h> #include <locale.h>

#define MAXL 255 /* макс. довжина рядка */

/* Якби мы не включили ctype.h, то повинні були б визначити:

* #define isspace(c) ((c) == ' ' || (c) == '\t' || (c) == '\f') */

main ( argc, argv ) char **argv;

{

setlocale(LC_ALL, ""); if( argc == 1 ){

/* программа викликани без аргументов */ munch( "" );

} else{

/* аргументы программи - імена файлів */ while( argv[ 1 ] ){

munch( argv[1] ); argv++;

argc--;

}

}

total();

exit(0);

}

/* обробити файл з іменем name */ munch( name ) char *name;

{

 

 

char

l[MAXL];

/* буфер для чергового рядка */

int len;

 

/* довжиною цього рядка */

char *words[50];

/* таблиця полів рядка */

char **s;

/* службова */

int nwords;

/* число слів в рядку */

FILE *fp;

if( name == NULL || !*name )

fp = stdin; /* стандартний ввід */

200

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]