Читайте также:
|
|
Щоб показати, як можна легко записувати великі об'єми даних, користуючись функціями fread () і fwrite (), ми розглянемо програму роботи зі списком розсилки. Програма зможе зберігати адреси у файлі. Адреси будуть зберігатися в масиві структур наступного типу:
struct addr
{
char name [30];
char street [40];
char city [20];
char state [3];
unsigned long int zip;
} Addr_list [MAX];
Значення MAX визначає максимальну кількість адрес, що може бути у списку.
При виконанні програми поле name кожної структури ініціалізується порожнім вказівником (NULL). У програмі вільною вважається та структура, поле name якої містить рядок нульової довжини, тобто ім'я адресата являє собою порожнім рядком.
Далі наведені функції save () і load (), які використовуються відповідно для збереження і завантаження бази даних (списку розсилки). Зверніть увагу, наскільки коротко вдалося закодувати кожну з функцій, а адже ця стислість досягнута завдяки потужності fread () і fwrite ()! І ше зверніть увагу на те, як ці функції перевіряють значення, що повертаються функціями fread () і fwrite (), щоб виявити таким чином можливі помилки.
/* Збереження списку. */
void save (void)
{
FILE *fp;
register int i;
if ((fp = fopen ("maillist", "wb")) == NULL)
{
printf ("Помилка при вiдкриттi файлу. \n");
return;
}
for (i = 0; i <MAX; i++)
if (*addr_list[i].name)
if (fwrite (&addr_list[i], sizeof (struct addr), 1, fp)!= 1)
printf ("Помилка при записi файлу. \n");
fclose (fp);
}
/* Завантажити файл. */
void load (void)
{
FILE *fp;
register int i;
if ((fp = fopen ("maillist", "rb")) == NULL)
{
printf ("Помилка при вiдкриттi файлу. \ n");
return;
}
init_list ();
for (i = 0; i <MAX; i++)
if (fread (&addr_list[i], sizeof (struct addr), 1, fp)!= 1)
{
if (feof (fp)) break;
printf ("Помилка при читаннi файлу. \n");
}
fclose (fp);
}
Обидві функції, save () і load (), підтверджують (або не підтверджують) успішність виконання функціями fread () і fwrite () операцій з файлом, перевіряючи значення, що повертаються функціями fread () і fwrite (). Крім того, функція load () явно перевіряє, чи не досягнуто кінець файлу. Робить вона це за допомогою виклику функції feof (). Це доводиться робити тому, що fread () і в разі помилки, і при досягненні кінця файлу повертає одне і те ж значення.
Далі показана вся програма, що обробляє списки розсилки. Її можна використовувати як ядро для подальших розширень, в неї, наприклад, можна додати засоби пошуку адрес.
/* Проста програма обробки списку розсилки,
в якій використовується масив структур. */
# include <windows.h>
# include <locale.h>
# include <stdio.h>
# include <stdlib.h>
# define MAX 100
struct addr
{
char name [30];
char street [40];
char city [20];
char state [20];
unsigned long int zip;
} addr_list [MAX];
void init_list (), enter ();
void delete1 (), list ();
void load (), save ();
int menu_select (), find_free ();
int main()
{
setlocale(LC_CTYPE, "Russian");
char choice;
init_list (); /* ініціалізація масиву структур */
for (;;)
{
choice = menu_select ();
switch (choice)
{
case 1: enter (); break;
case 2: delete1 (); break;
case 3: list (); break;
case 4: save (); break;
case 5: load (); break;
case 6: exit (0);
}
}
system ("pause");
return 0;
}
/* Ініціалізація списку. */
void init_list ()
{
register int t;
for (t = 0; t <MAX; ++t) addr_list [t].name[0] = '\0';
}
/* Отримання значення, вибраного в меню. */
int menu_select ()
{
char s [80];
int c;
printf ("1. Ввести iм'я \n");
printf ("2. Видалити iм'я \n");
printf ("3. Вивести список \n");
printf ("4. Зберегти файл \n");
printf ("5. Завантажити файл \n");
printf ("6. Вихiд \n");
do
{
printf ("\n Введiть номер потрiбного пункту: ");
gets (s);
c = atoi (s);
} while (c <0 || c> 6);
return c;
}
/* Додавання адреси в список. */
void enter ()
{
int slot;
char s [80];
slot = find_free ();
if (slot == -1)
{
printf ("\n Список заповнений");
return;
}
printf ("Введiть iм'я: ");
gets (addr_list [slot].name);
printf ("Введiть вулицю: ");
gets (addr_list [slot].street);
printf ("Введiть мiсто: ");
gets (addr_list [slot].city);
printf ("Введiть Область: ");
gets (addr_list [slot].state);
printf ("Введiть поштовий iндекс: ");
gets (s);
addr_list [slot].zip = strtoul (s, '\0', 10);
}
/* Пошук вільної структури. */
int find_free ()
{
register int t;
for (t = 0; addr_list[t].name [0] && t <MAX; ++t);
if (t == MAX) return -1; /* вільних структур немає */
return t;
}
/* Видалення адреси. */
void delete1 ()
{
register int slot;
char s [80];
printf ("Введiть № запису: ");
gets (s);
slot = atoi (s);
if (slot >= 0 && slot <MAX)
addr_list [slot].name [0] = '\0';
}
/* Вивід списку на екран. */
void list ()
{
register int t;
for (t = 0; t <MAX; ++t)
{
if (addr_list[t].name [0])
{
printf ("%s \n", addr_list[t].name);
printf ("%s \n", addr_list[t].street);
printf ("%s \n", addr_list[t].city);
printf ("%s \n", addr_list[t].state);
printf ("%lu \n \n", addr_list[t].zip);
}
}
printf ("\n \n");
}
/* Збереження списку. */
void save ()
{
FILE *fp;
register int i;
if ((fp = fopen ("maillist", "wb")) == NULL)
{
printf ("Помилка при вiдкриттi файлу. \n");
return;
}
for (i = 0; i <MAX; i++)
if (*addr_list[i].name)
if (fwrite (&addr_list[i], sizeof (struct addr), 1, fp)!= 1)
printf ("Помилка при записi файлу. \n");
fclose (fp);
}
/* Завантажити файл. */
void load ()
{
FILE *fp;
register int i;
if ((fp = fopen ("maillist", "rb")) == NULL)
{
printf ("Помилка при вiдкриттi файлу. \ n");
return;
}
init_list ();
for (i = 0; i <MAX; i++)
if (fread (&addr_list[i], sizeof (struct addr), 1, fp)!= 1)
{
if (feof (fp)) break;
printf ("Помилка при читаннi файлу. \n");
}
fclose (fp);
}
Функция fseek()
Формат функції:
fseek(fp, n, m);
Ця функція встановлює вказівник у файлі fp в позицію, що відстоїть на n байт від поточної, а напрямок переміщення (вперед або назад) визначається параметром m, який може бути заданий одним з трьох значень: 0, 1, 2 або однією з трьох символьних констант, визначених у файлі stdio.h:
- SEEK_SET = 0 — до початку файлу;
- SEEK_CUR = 1 — вказівник на поточну позицію у файлі;
- SEEK_END = 2 — до кінця файлу.
Функція fseek () використовується для введення/виведення потоком. Для роботи не з потоковими даними слід використовувати функцію lseek (). Після функції fseek () можна виконувати операції оновлення у файлах, відкритих для поновлення. При вдалому завершенні роботи fseek () повертає нуль, в іншому випадку функція повертає код помилки. Тоді глобальна змінна errno приймає одне з наступних значень:
- EBADF - невірний вказівник файлу;
- EINVAL - невірний аргумент функції;
- ESPIPE - пошук на пристрої заборонений.
При прямому доступі можна виконувати операції ведення/виведення, використовуючи систему введення/виведення мови С і функцію fseek (), яка встановлює вказівник поточної позиції у файлі. Ось прототип цієї функції:
int fseek (FILE <вказівник файлу>, long int <кількість_байт>, <початок_відліку>);
кількість_байт - кількість байтів, рахуючи від початок_відліку, воно визначає нове значення вказівника поточної позиції, а початок відліку - це один з наступних макросів:
Початок відліку - Макрос
Початок файлу - SEEK_SET
Поточна позиція - SEEK_CUR
Кінець файлу - SEEK_END
Тому, щоб отримати у файлі доступ на відстані кількість_байт байтів від початку файлу, початок_відліку повинен дорівнювати SEEK_SET. Щоб при доступі відстань відраховувалася від поточної позиції, використовуйте макрос SEEK_CUR, а щоб при доступі відстань відраховувалася від кінця файлу, потрібно вказувати макрос SEEK_END. При успішному завершенні своєї роботи функція fseek () повертає нуль, а в разі помилки - ненульове значення.
У наступній програмі показано, як використовується fseek (). Дана програма в певному файлі відшукує деякий байт, а потім відображає його. У командному рядку потрібно вказати ім'я файлу, а потім потрібний байт, тобто його відстань в байтах від початку файлу.
# include <stdlib.h>
# include <stdio.h>
# include <windows.h>
# include <locale.h>
int main ()
{
setlocale(LC_CTYPE, "Russian");
FILE * fp;
char ch;
if ((fp = fopen ("testr", "r")) == NULL)
{
printf ("Помилка при вiдкриттi файлу. \ n");
system ("pause");
exit (1);
}
fseek (fp, 6, SEEK_SET);
ch=fgetc (fp);
printf ("У 6-му байтi мiститься ");
putchar (ch);
printf ("\n");
fclose (fp);
system ("pause");
return 0;
}
Функцію fseek () можна використовувати для доступу всередині багатьох значень одного типу, просто множачи розмір даних на номер елемента, який вам потрібен. Наприклад, припустимо, є список розсилки, який складається з структур типу addr (визначених раніше). Щоб отримати доступ до десятої адреси у файлі, в якому зберігаються адреси, використовуйте наступний оператор:
fseek (fp, 9 * sizeof (struct addr), SEEK_SET);
Поточне значення вказівника поточної позиції у файлі можна визначити за допомогою функції ftell (). Ось її прототип:
long int ftell (FILE <вказівником файлу>);
Функція повертає поточне значення вказівника поточної позиції у файлі, пов'язаному з вказівником файлу вф. При невдалому результаті вона повертає -1.
Зазвичай прямий доступ може знадобитися лише для двійкових файлів. Причина тут проста - тому що в текстових файлах можуть виконуватися перетворення символів, то може і не бути прямої відповідності між тим, що знаходиться в файлі і тим байтом, до якого потрібен доступ. Єдиний випадок, коли треба використовувати fseek () для текстового файлу - це доступ до тієї позиції, яка була вже знайдена за допомогою ftell (); такий доступ виконується за допомогою макросу SEEK_SET, використовуваного в якості початку відліку.
Добре пам'ятайте наступне: навіть якщо в файлі знаходиться один тільки текст, все одно цей файл при необхідності можна відкрити і в двійковому режимі. Ніякі обмеження, пов'язані з тим, що файли містять текст, до операцій прямого доступу не відносяться. Ці обмеження стосуються лише до файлів, відкритих в текстовому режимі.
Функция ftell()
Формат функції:
long int i = ftell(fp);
Повертає поточне значення вказівника файлу fp (тобто номер поточної позиції) у вигляді значення типу long int. Відлік йде в байтах від початку файлу.
Повернене значення може бути використане потім у функції fseek ().
Якщо виявлені помилки, то функція повертає значення -1L і привласнює глобальній змінній errno додатне значення.
# include <stdlib.h>
# include <stdio.h>
# include <windows.h>
# include <locale.h>
# include <string.h>
int main()
{
setlocale(LC_CTYPE, "Russian");
char str [80];
int i=0;
long int l;
FILE *fp;
if ((fp = fopen ("test", "r")) == NULL)
{
printf ("Помилка при вiдкриттi файлу. \n");
system ("pause");
exit(1);
}
while (! feof (fp))
{
l = ftell(fp);
printf ("Номер поточної позицiї %d перед зчитуванням ", l);
fgets (str, 79, fp);
printf (str);
}
l = ftell(fp);
printf ("\n Номер поточної позицiї %d пiсля зчитування ", l);
printf ("\n");
fclose (fp);
system ("pause");
return 0;
}
Функция fscanf()
Формат функції:
fscanf(fp, Control, arg1, arg2,..., argn);
Функція читає дані з файлу з вказівником fp, перетворює їх за форматами, записаними у керуючому рядку Control, і відформатовані дані записує в аргументи arg1,..., аrgn. Докладні відомості про роботу цієї функції можна отримати, ознайомившись з роботою функції scanf () (функцію scanf ()).
Функция fprintf()
Формат функції:
fprinf(fp, Control, arg1, arg2,..., argn);
Виводить дані в файл з вказівником fp, перетворює аргументи arg1,..., аrgn до форматів, які записані в керуючому рядку Control, і відформатовані дані записує в файл. Докладні відомості про роботу цієї функції можна отримати, ознайомившись з роботою функції printf ().
Крім основних функцій введення/виведення, про які йшла мова, в системі введення/виведення мови С також є функції fprintf () і fscanf (). Ці дві функції, за винятком того, що призначені для роботи з файлами, ведуть себе точно так само, як і printf () та scanf (). Прототипи функцій fprintf () і fscanf () наступні:
int fprintf (FILE * вф, const char * керуючий_рядок,...);
int fscanf (FILE * вф, const char * керуючий_рядок,...);
де вф - вказівник файлу, що повертається в результаті виклику fopen (). Операції введення/виведення функції fprintf () і fscanf () виконують з тим файлом, на який вказує вф.
В якості прикладу пропонується розглянути наступну програму, яка читає з клавіатури рядок і ціле значення, а потім записує їх у файл на диску; ім'я цього файлу – test1. Після цього програма читає цей файл і виводить інформацію на екран. Після запуску програми перевірте, яким вийде файл test1. Як ви і побачите, в ньому буде цілком легкий для читання текст.
/* Приклад використання fscanf () і fprintf () */
# include <stdlib.h>
# include <stdio.h>
# include <windows.h>
# include <locale.h>
int main ()
{
setlocale(LC_CTYPE, "Russian");
FILE *fp;
char s [80];
int t;
if ((fp = fopen ("test1", "w")) == NULL)
{
printf ("Помилка при вiдкриттi файлу. \ n");
system ("pause");
exit (1);
}
printf ("Введiть рядок i число: ");
fscanf (stdin, "%s %d", s, &t); /* читати з клавіатури */
fprintf (fp, "%s %d", s, t); /* писати в файл */
fclose (fp);
if ((fp = fopen ("test1", "r")) == NULL)
{
printf ("Помилка при вiдкриттi файлу. \n");
system ("pause");
exit (1);
}
printf("\n");
fscanf (fp, "%s %d", s, &t); /* читання з файлу */
fprintf (stdout, "%s %d", s, t); /* виведення на екран */
printf("\n");
fclose (fp);
system ("pause");
return 0;
}
Маленьке попередження. Хоча читати різносортні дані з файлів на дисках і писати їх у файли, розташовані також на дисках, часто легше всього саме за допомогою функцій fprintf () і fscanf (), але це не завжди найефективніший спосіб виконання операцій читання і запису. Так як дані в форматі ASCII записуються так, як вони повинні з'явитися на екрані (а не в двійковому вигляді), то кожен виклик цих функцій пов'язаний з певними накладними витратами. Тому, якщо треба піклуватися про розмір файлу або швидкості, то, швидше за все, доведеться використовувати fread () і fwrite ().
Функция rewind()
Формат функції:
rewind(<вказівник_на_файл>);
Встановлює вказівник поточної позиції у файлі на початок файлу. Виклик функції rewind (<вказівник_на_файл>) відповідає виклику функції fseek (<вказівник_на_файл>, 0L, SEEK_SET) за винятком того, що rewind () скидає індикатор кінця файлу і індикатори помилок, а fseek () скидає тільки індикатор кінця файлу. Після функції rewind () можна виконувати операції оновлення для файлів, відкритих для поновлення. Функція не повертає ніякого значення.
Щоб познайомитися з rewind (), змінимо програму, розглянуту раніше, таким чином, щоб вона відображала вміст файлу відразу після його створення. Щоб виконати відображення, програма після завершення введення "перемотує" файл, а потім читає його з самого початку. Зверніть увагу, що зараз файл необхідно відкрити в режимі читання/запису, використовуючи як аргумент режим, що задає рядок "w+".
# include <stdlib.h>
# include <stdio.h>
# include <windows.h>
# include <locale.h>
# include <string.h>
int main()
{
setlocale(LC_CTYPE, "Russian");
char str [80];
FILE *fp;
if ((fp = fopen ("TEST", "w+")) == NULL)
{
printf ("Помилка при вiдкриттi файлу. \ n");
system ("pause");
exit(1);
}
do
{
printf ("Введiть рядок (порожнiй рядок - для виходу): \n");
gets (str);
strcat (str, "\n"); // додавання роздільника рядків
fputs (str, fp);
} while (strcmp("\n", str)); // strcmp() - порівняння рядків
/* Тепер виконується читання і відображення файлу */
rewind (fp); /* встановити вказівник поточної позиції на початок файлу. */
while (! feof (fp))
{
fgets (str, 79, fp);
printf (str);
}
fclose (fp);
system ("pause");
return 0;
}
Функция remove()
Формат функції:
int remove (< ім'я_файлу>);
Функція видаляє файл з ім'ям FILENameString. Перед видаленням файл повинен бути закритий. Рядок FILENameString повинен містити повний шлях до файлу. При нормальному завершенні завдання функція повертає нуль, а в разі помилки – 1 (ненульове значення). При цьому глобальна змінна errno приймає наступні значення:
- EACCES - заборонено видаляти;
- ENOENT - немає такого файлу або директорії.
Наступна програма вилучає файл. Однак спочатку вона дає можливість передумати. Утиліта, подібна цій, може стати в нагоді комп'ютерним користувачам-новачкам.
/* Подвійна перевірка перед вилученням. */
# include <stdlib.h>
# include <stdio.h>
# include <windows.h>
# include <locale.h>
# include <ctype.h>
int main()
{
setlocale(LC_CTYPE, "Russian");
char str [80];
char filename[20]={"test"};
printf ("Вилучити файл test? (y/n):");
gets (str);
if (toupper (*str) == 'y')
remove (filename);
if (remove (filename))
{
printf ("Не можна вилучити файл. \n");
system ("pause");
exit (1);
}
system ("pause");
return 0;
}
Функция FILElength()
Формат функции:
long FILElength(fp);
Функція повертає довжину файлу з вказівником fp в байтах. Для роботи функції потрібно підключити файл io.h. У випадку помилки функція повертає - 1, та глобальна змінна errno набуває значення EBADF - невірний вказівник файлу.
Функция ferror()
Формат функції:
ferror(fp);
Функція ferror() визначає, чи відбулася помилка під час виконання операції з файлом. Прототип цієї функції наступний:
int ferror(FILE < вказівник_файлу >);
Функція повертає значення true (істина), якщо при останній операції з файлом сталася помилка, в іншому ж випадку вона повертає false (неправда). Так як при будь-якій операції з файлом встановлюється своя умова помилки, то після кожної такої операції слід одразу викликати ferror (), а інакше дані про помилці можуть бути втрачені.
У наступній програмі показано застосування ferror (). Програма видаляє табуляції з файлу, замінюючи їх пробілами. Розмір табуляції визначається макросом TAB_SIZE. Зверніть увагу, що ferror () викликається після кожної операції з файлом.
/* Програма замінює в текстовому файлі символи табуляції пробілами і відслідковує помилки. */
# include <stdlib.h>
# include <stdio.h>
# include <windows.h>
# include <locale.h>
# define IN 0
# define OUT 1
void err (int e);
int main()
{
setlocale(LC_CTYPE, "Russian");
FILE *in, *out;
int i;
char ch;
if ((in = fopen ("testr", "rb")) == NULL)
{
printf ("Не можна вiдкрити файл testr. \n");
system ("pause");
exit(1);
}
if ((out = fopen ("testw", "wb")) == NULL)
{
printf ("Не можна вiдкрити файл testw. \n");
system ("pause");
exit(1);
}
do
{
ch = fgetc (in);
if (ferror (in)) err (IN);
/* Якщо знайдена табуляція, виводиться пробіл */
if (ch == '\t')
{
putc (' ', out);
if (ferror (out)) err (OUT);
}
else
{
putc (ch, out);
if (ferror (out)) err (OUT);
}
} while (!feof (in));
fclose (in);
fclose (out);
system ("pause");
return 0;
}
void err (int e)
{
if (e == IN) printf ("Помилка при введеннi. \n");
if (e == OUT) printf ("Помилка при виведеннi. \n");
system ("pause");
exit (1);
}
Функция freopen()
Формат функції:
FILE *freopen(const char *FILEname, const char *mode, FILE *stream);
Функція підставляє файл, заданий в першому параметрі, замість уже відкритого потоку. Вона закриває потік незалежно від того, чи відкритий він. Ця функція корисна для заміни файлу, пов'язаного зі стандартними пристроями введення/виведення stdin, stdout або stderr. Способи відкриття файлу аналогічні функції fopen (). При успішному завершенні функція повертає вказівник типу FILE (як і функція fopen ()), при неуспішному - NULL.
Приклад перенаправлення потоку за допомогою функції freopen () наведено в лістингу 1.
Дата добавления: 2015-07-10; просмотров: 176 | Нарушение авторских прав
<== предыдущая страница | | | следующая страница ==> |
Функції для роботи з файлами | | | Функції стандартного введення/виведення |