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

Зубенко, Омельчук - Програмування. Поглиблений курс

.pdf
Скачиваний:
58
Добавлен:
07.03.2016
Размер:
5 Мб
Скачать
☆

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

Функції форматованого читання з файла й запису у файл:

int fscanf (FILE *stream, const char *format[,address, …]); int fprintf(FILE *stream, const char *format[,argument, …]);

Дані функції зі змінною кількістю параметрів є аналогами консоль- них функцій scanf та printf і працюють не зі стандартними, а з дові- льними відкритими потоками. Перша читає символи з потоку stream, відкритого для читання, перетворює їх згідно зі специфікаторами формату на двійкову форму й результати записує в поля, задані адре- сними аргументами. Друга здійснює виведення в потік stream, від- критий для запису, відформатованої інформації. Роль форматного рядка в обох функціях така сама, як і в їхніх аналогах.

Функції для роботи з буферним покажчиком:

long int ftell(FILE *stream);

int fseek(FILE *stream, long offset, int whence); int rewind(FILE *stream);

Функція ftell повертає поточну позицію буферного покажчика по- току stream, з якого починається наступна операція введення- виведення. Зокрема, це значення можна передати функції fseek. Ви- користовується також для текстових файлів, але тоді значення, що повертається, не обов'язково буде подавати байтове зміщення у фай- лі. Функція fseek установлює поточну позицію буферного покажчика потоку stream, з якої почнеться наступна операція читання чи запису. У випадку успіху повертає нуль, при виникненні помилки – ненульове значення. Тут long offset для двійкових файлів – кількість байтів, на яку потрібно зсунути файловий покажчик у напрямку, указаному whence. Для текстових файлів це значення має бути нулем чи значен- ням, що повертається функцією ftell. Щоб перемістити файловий покажчик в оберненому напрямку (до початку файла), потрібно вста- новити значення offset від'ємним. Для переміщення файлового по- кажчика на offset байтів від початку файла потрібно встановити whence рівним SEEK_SET (або 0) відносно поточної позиції – SEEK_CUR (або 1); на задану кількість байтів від кінця – SEEK_END (або 2).

Функція rewind установлює буферний покажчик на початок файла. Зазвичай використовується для повторного читання, починаючи з першого байта, чи для підготовки запису нових даних із початку іс- нуючого файла.

401

ПРОГРАМУВАННЯ

Запис буферів у файл:

int fflush(FILE *stream);

Функція записує у файл поточний зміст буфера для заданого пото- ку без закривання файла. Цю функцію слід періодично викликати в усіх програмах, що довго працюють із відкритими файлами.

Повідомлення про помилкові ситуації в потоці:

int ferror(FILE *stream);

Функція повертає істину (ненульове значення), якщо під час оброб- ки даного потоку виникали помилки. Якщо помилки не були виявлені, то повертається нуль (хибно).

Видалення файла:

int remove(const char *filename);

Функція видаляє файл filename з файлової системи за умови, що він існує. У випадку успіху повертає нуль, інакше повертає –1 і вста- новлює значення змінної errno рівним ENOENT (файл не знайдено) або EACCES (доступ заборонено). Зазвичай реалізується як макрос, що ви- кликає функцію unlink.

Функції для читання й запису даних із потоку:

size_t fread (void *ptr, size_t size, size_t n, FILE *stream); size_t fwrite(void *ptr, size_t size, size_t n, FILE *stream);

Реалізують введення-виведення нижнього рівня. Функція fread чи- тає дані з файлового потоку, відкритого у двійковому режимі, почи- наючи з поточного файлового покажчика. Після читання даних цією функцією файловий покажчик розміщується безпосередньо за остан- нім прочитаним байтом. Кількість прочитаних байтів дорівнює ре- зультату функції, помноженому на кількість байтів у елементі. У ви- падку невдачі або якщо файловий покажчик розташований за кінцем файла функція повертає нуль. Тут void *ptr – покажчик на прийма- ючий буфер, size_t size – розмір у байтах одного елемента, що чита- ється з файлового потоку, size_t n – кількість таких елементів.

Функція fwrite записує один чи кілька байтів у файловий потік, ві- дкритий у двійковому режимі, і повертає кількість записаних елемен- тів. Кількість записаних байтів дорівнює результату функції, помно-

402

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

женому на значення аргументу size (розмір одного елемента). Тут void *ptr – покажчик на дані, що записуються, size_t size – розмір у байтах одного елемента, size_t n – кількість елементів.

Приклад 3.73. Для ілюстрації роботи деяких описаних вище фун- кцій розглянемо програму:

/*fopen, fgets, ftell, fseek, fread, fclose, rewind, ferror, fputs*/

#include <stdio.h> #include <stdlib.h> #define FNAME "TEST.C"

int main(int argc, char *argv[])

{

FILE *inf; /*вхідний файловий потік*/ long pos, pos1; /*позиція у файлі*/ char buffer[128];

char c;

/*відкрити текстовий файл для читання у двійковому режимі*/ inf=fopen(FNAME, "rb");

if(inf==NULL){

printf("Помилка при відкриванні файла", FNAME); exit(1);

}

/*читає перший рядок*/ fgets(buffer, 128, inf); printf("10-й рядок==%s\n", buffer); pos=ftell(inf);

printf("Позиція у файлі==%ld\n", pos); /*покажчик на початок файла*/ rewind(inf);

pos1=ftell(inf);

printf("Позиція після rewind==%ld\n", pos1);

/*Шукати pos байтів від початку файла*/ if(fseek(inf, pos-1, SEEK_SET)!=0)

printf("Помилка позиціонування");

else{

/*читання символу з файла*/ fread(&c, 1, 1, inf); printf("pos-й символ==%c", c);

}

/*намагаємося писати у відкритий для читання файл, тобто моделюємо помилку*/

403

ПРОГРАМУВАННЯ

puts("Намагаємося писати у відкритий для читання файл");

fputs("Моделюємо файлову помилку", inf); if(ferror(inf))

printf("Виникла помилка файлового потоку!");

/*закривання файла*/ fclose(inf);

return 0;

}

/*fopen, fwrite, rewind, fread, fclose*/ #include <stdio.h>

#include <stdlib.h>

int array[100]; /*масив цілих нулів*/ #define FNAME "TEMP.C"

int main(int int argc, char *argv[])

{

/*створити файл у режимі w+b*/ FILE *tmpf=fopen(FNAME, "w+b"); if(!tmpf){

perror("Не можна відкрити файл"); exit(1);

}

/*записати 100 цілих у файл*/ for(int i=0; i<100; i++)

fwrite(&i, sizeof(int), 1, tmpf); /*прочитати цілі числа в масив*/

/*установити файловий покажчик на початок файла*/ rewind(tmpf);

fread(&array, sizeof(int), 100, tmpf); puts("Масив після читання з диска:\n"); for(int i=0; i<100; i++)

printf("%8d", array[i]); /*закрити файл*/

fclose(tmpf); return 0;

}

■

3.8.4. ПЕРЕНАПРАВЛЕННЯ ПОТОКІВ

Пригадаємо, що на початку виконання програми автоматично від- криваються три потоки: stdіn (стандартний потік введення), stdout (стандартний потік виведення) і stderr (стандартний потік помилок).

404

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

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

Функції консольного введення-виведення насправді спрямовують результати своїх операцій на один із потоків – stdіn або stdout, і по суті, кожна з них є спеціальною версією відповідної файлової функції.

Оскільки імена стандартних потоків є покажчиками файлів типу FІLE*, то вони можуть використовуватися для застосування загальних файлових операцій при введенні-виведенні на консоль. Наприклад, функція putchar може бути визначена таким чином:

іnt putchar(char c)

{

return putc(c, stdout);

}

Узагалі, у ролі покажчиків файлів потоки stdіn, stdout та stderr можна застосовувати в будь-якій функції, де використовується змін- на типу FІLE*. Наприклад, для введення рядка з консолі можна засто- сувати такий виклик файлової функції fgets:

char str[255]; fgets(str, 80, stdіn);

Зазначимо, що stdіn, stdout і stderr – це не змінні у звичайному ро- зумінні та їм не можна присвоювати значення за допомогою функції fopen(). Через те, що на початку роботи програми покажчики файлів створюються автоматично, наприкінці роботи вони й закриваються автоматично. Тому не потрібно намагатися закрити їх самостійно.

Операції введення-виведення на дискових файлах можна викону- вати за допомогою функції консольного введення-виведення, напри- клад prіntf. Справа в тому, що всі функції консольного введення- виведення виконують свої операції з потоками stdіn та stdout, а в середовищах, що підтримують перенаправлення потоків введення- виведення, це рівносильне тому, що stdіn або stdout можуть бути пе- ренаправлені на інший пристрій. Коли С-програма завершується, то всі перенаправлені потоки повертаються в стани, які були встановле- ні за умовчанням. Таке перенаправлення здійснює функція

FІLE *freopen(const char *filename, const char *mode, FІLE *stream);

Вона закриває поточний відкритий файловий потік і пов'язує його з но- вим файлом. Зазвичай використовується для перенаправлення потоків

405

ПРОГРАМУВАННЯ

stdin, stdout, stderr. Тут filename – покажчик на рядок імені нового файла, пов'язаного з файловим потоком. Файл відкривається в режимі mode; цей параметр може набувати тих самих значень, що й відповідний параметр функції fopen(). Якщо функція freopen() виконана успішно, то вона повертає stream, а якщо зустрілися помилки – то NULL:

#include <stdio.h> #include <stdlib.h> #include <string.h>

int main()

{

FILE *outf;

//перенаправити stderr на тимчасовий вихідний файл outf=freopen("tmp.txt", "w", stderr); if(outf==NULL) {

puts("Помилка відкривання тимчасового файла"); exit(1);

}

fputs("Перевірка повідомлення про помилку,\n", stderr); fputs("записаного у файл за допомогою stderr\n", stderr); fclose(outf);

printf("Перегляньте файл tmp.txt");

return 0;

}

Перенаправлення стандартних потоків за допомогою freopen у де- яких випадках може бути корисним, наприклад, при налагодженні. Однак виконання дискових операцій введення-виведення на перена- правлених потоках stdіn та stdout не дуже ефективне порівняно з використанням таких функцій, як fread або fwrіte.

* Література для CР: обробка файлів – [32, 55, 102, 133].

Контрольні запитання та вправи

1.Що таке введення-виведення для консолі й портів?

2.Які функції є консольними функціями введення-виведення?

3.Як ввести символ із клавіатури?

4.Яка різниця в дії функцій getchar, getch та getchе?

5.Як вивести символ на екран?

6.Як прочитати рядок символів із клавіатури?

7.Як вивести на екран рядок "Hello, world!"?

8.Що таке форматовані введення й виведення?

406

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

9.Описати форматний рядок функції printf.

10.Описати форматний рядок функції scanf.

11.Як діють функції sscanf та sprintf?

12.Що таке потік і файл?

13.Що таке потокове введення-виведення?

14.Що таке покажчик файла і для чого він використовується?

15.Як відкрити файловий потік?

16.Які є режими відкривання файла?

17.Як закрити файловий потік?

18.Як записати символ у потік?

19.Як отримати символ із заданого потоку?

20.Як записати рядок у потік?

21.Як отримати рядок із заданого потоку?

22.Як отримати файловий покажчик?

23.Як установити нову позицію покажчика файла?

24.Як установити файловий покажчик на початок файла?

25.Як здійснити форматоване текстове виведення чисел у файл? Яка роль при цьому форматного рядка?

26.Як прочитати з файла п'ять даних різних числових типів?

27.Як перевірити, чи досягнутий кінець файла?

28.Як перевірити, чи виникали помилки під час обробки фай- лового потоку?

29.Як видалити файл?

30.Описати функції для читання й запису даних fread та fwrіte.

31.Що таке перенаправлення потоків?

32.Написати програму-фільтр, яка малі латинські літери замі- нює на великі. Текст вводиться з клавіатури й виводиться на екран. Скористатися функціями islower(с) та

toupper(c). Перша перевіряє, чи є символ малою латинсь- кою літерою, друга повертає код відповідної літери на вер- хньому регістрі.

33. Підрахувати кількість прочитаних із клавіатури: а) симво- лів; б) символів табуляції (‘\t'), пробілів (‘ ‘) і рядків (‘\n'). Для завершення процесу читання ввести символ

‘Ctrl+Z'.

34.Написати програму-фільтр, яка копіює введені з клавіатури символи на екран і при цьому: а) прибирає зайві пробіли; б) розміщує по одному слову на рядок. В обох випадках підра- хувати кількість введених із клавіатури слів.

35.Не всі зовнішні пристрої вміють інтерпретувати символ та- буляції ‘\t'. Написати програми-фільтри для вилучення символу ‘\t' текстового файла й заміни його відповідною кількістю пробілів ‘ ' і навпаки – для вставки там це можли- во символ табуляції і вилученням відповідної кількості про-

407

ПРОГРАМУВАННЯ

білів. Позиції для табуляції задаються константою n у ко- мандному рядку. Стандартне значення константи – 8.

36.Із клавіатури читається послідовність рядків. Надрукувати

їїв лексикографічному порядку.

37.Написати програму з іменем tail, що друкує n останніх

введених рядків. За умовчанням n=5. Однак n може бути

задано й у командному рядку. Командний рядок > tail – n запускає друкування останніх n рядків. У кожний момент зберігати в пам'яті не більше n рядків.

38.Написати програму-фільтр для копіювання текстового фай- ла. Рядки тексту мають довжину не більше 80. Порожні ря- дки й рядки із символів-пробілів вилучаються. Вхідний файл: а) вводиться з клавіатури; б) міститься на диску.

39.Порівняти два файли й надрукувати перший рядок, в яко- му вони розрізняються.

40.Написати: а) довільну програму, б) * програму з лінійною часовою складністю пошуку текстових файлів, що містять входження даного слова. Слово та імена файлів подаються в командному рядку.

41.Написати програму для друкування кількох файлів. Кожний файл повинен розпочинатися з нової сторінки, містити за- головок і мати свою нумерацію сторінок. Розміри сторінки задаються у командному рядку.

42.Написати функцію для: а) копіювання файла в список його елементів у ОП; б) переписування у файл елементів задано- го списку.

43.Написати програму для пошуку спільних рядків двох упо- рядкованих лексикографічно файлів. Програма має виво- дити рядки у три колонки: у першу – ті, що є тільки в пер- шому файлі; у другу – ті, що є тільки в другому; у третю – спільні рядки обох файлів.

44.Написати програму, що будує дерево пошуку з чисел, пода- них у файлі sf.txt, і виводить його: а) у префіксному; б) постфіксному; в) інфіксному порядках у файл df.txt.

45.Прокоментувати й налагодити програму:

#include <stdio.h> #include <stdlib.h> #define N 5

int array[N];

#define FNAME "TEMP.C"

void main()

{

408

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

FILE *tmpf=fopen(FNAME, "w+b");

/* …

*/

if(!tmpf){perror("Не можна відкрити файл");

exit(1);

 

 

 

}

 

 

 

for(int i=0; i<N; i++)

/* …

*/

fwrite(&i, sizeof(int), 1, tmpf);

rewind(tmpf); /* …

*/

 

/* … */

fread(&array, sizeof(int), 100, tmpf);

puts("Масив після читання з диска:\n");

 

for(int i=0; i<100; i++)printf("%8d", array[i]);

fclose(tmpf);

 

 

 

}

 

 

 

46 * . Написати функції

int fwritef(FILE* fp,char

*format,…) та int freadf(FILE* fp, char *format,…)

для запису в потік і читання з потоку послідовностей різ- нотипних даних. Скористатися макросами з бібліотеки <stdarg.h> для роботи з неоголошеними аргументами функції (див. підрозд. 3.6.7). Функції повертають значен- ня так само, як їхні аналоги – функції fprintf та fscanf.

3.9. Препроцесор

¾Директиви препроцесора

¾Макропідстановки

¾Включення файлів

¾Умовна компіляція

¾Директиви #error, #line та #pragma

Ключові слова: препроцесор, директива препроцесора, простий і параметри- зований макроси, включення файлів, умовна компіляція, операції перетворення на рядок # і конкатенації ##, директиви: #define, #endif, #ifdef, #line, #elif,

#error, #ifndef, #pragma, #else, #if, #include, #undef.

Препроцесор є складовою системи програмування мови C. Він отримує на вхід текст С-програми з директивами, виконує їх і вилу- чає з тексту. Окрім цього, фаза препроцесування включає:

1) відкидання пари символів: \ та \n (склеювання сусідніх рядків програми);

409

ПРОГРАМУВАННЯ

2)розбиття програми на лексеми. Коментарі при цьому замінюють- ся одиничними пробілами;

3)заміну в символьних константах і літералах керівних послідовно- стей на їхні еквіваленти. Сусідні літерали конкатенуються.

Розглянемо приклад препроцесорної обробки C-програми, що не міс- тить директив препроцесора. Лексеми розміщуються в дужках <…>.

Приклад 3.74. Нехай на вхід препроцесора надходить програма:

int main(void)

{

printf("\n"); /*вихід із програми*/

}

Препроцесор поверне послідовність лексем:

<int>

Службове слово

< >

Пробіл

<main>

Ідентифікатор

<(>

Роздільник

<void>

Ідентифікатор

<)>

Роздільник

<{>

Роздільник

< >

Пробіл (один замість трьох)

<printf>

Ідентифікатор

<(>

Роздільник

<10>

Код символу ‘\n'

<)>

Роздільник

<; >

Роздільник

< >

Пробіл замість коментаря

<}>

Роздільник

■

3.9.1. ДИРЕКТИВИ ПРЕПРОЦЕСОРА

Директиви задають певні синтаксичні перетворення тексту про- грами й мають вигляд:

#<ім'я-директиви> <лексема-операнд>{<лексема-операнд>}

Вони розміщуються в окремих рядках і розпочинаються символом ‘#'. Область дії директив – від неї до кінця текстового файла. Якщо безпосередньо перед закінченням рядка поставити символ ‘\', то ди- ректива продовжиться на наступний рядок програми.

410