русс | укр

Мови програмуванняВідео уроки php mysqlПаскальСіАсемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование


Linux Unix Алгоритмічні мови Архітектура мікроконтролерів Введення в розробку розподілених інформаційних систем Дискретна математика Інформаційне обслуговування користувачів Інформація та моделювання в управлінні виробництвом Комп'ютерна графіка Лекції


Приклад: втілення fopen і getc


Дата додавання: 2014-11-28; переглядів: 891.


Давайте проілюструємо як ці частини скласти разом шляхом втілення функцій стандартної бібліотеки fopen і getc.

Якщо пригадуєте, файли в стандартній бібліотеці описано за покажчиками на файл, а не дескрипторами. Покажчик на файл — це просто покажчик на структуру, яка містить декілька деталей інформації про файл: покажчик на буфер, тож файл можна читати великими відрізками; відлік кількості знаків, що залишились у буфері; покажчик на наступну позицію знака в буфері; дескриптор файла; і, нарешті, прапорці, що описують режим (читання/запису), статус помилки тощо.

Структура даних, що описує файл, міститься в <stdio.h>, який треба включити (за допомогою#include) в будь-який файл, що використовує функції зі стандартної бібліотеки вводу/виводу. Вона також включена в функції цієї бібліотеки. В наступному вирізку з типового <stdio.h>, назви, призначені для використання тільки функціями бібліотеки, починаються з жорсткого пробілу, тож зменшується ймовірність того, що вони збіжуться із назвами в користувацьких програмах. Ця умовність застосовується в усіх функціях стандартної бібліотеки.

#define NULL 0

#define EOF (-1)

#define BUFSIZ 1024

#define OPEN_MAX 20 /* max #files open at once */

 

typedef struct _iobuf {

int cnt; /* characters left */

char *ptr; /* next character position */

char *base; /* location of buffer */

int flag; /* mode of file access */

int fd; /* file descriptor */

} FILE;

extern FILE _iob[OPEN_MAX];

 

#define stdin (&_iob[0])

#define stdout (&_iob[1])

#define stderr (&_iob[2])

 

enum _flags {

_READ = 01, /* file open for reading */

_WRITE = 02, /* file open for writing */

_UNBUF = 04, /* file is unbuffered */

_EOF = 010, /* EOF has occurred on this file */

_ERR = 020 /* error occurred on this file */

};

 

int _fillbuf(FILE *);

int _flushbuf(int, FILE *);

 

#define feof(p) ((p)->flag & _EOF) != 0)

#define ferror(p) ((p)->flag & _ERR) != 0)

#define fileno(p) ((p)->fd)

 

#define getc(p) (--(p)->cnt >= 0 \

? (unsigned char) *(p)->ptr++ : _fillbuf(p))

#define putc(x,p) (--(p)->cnt >= 0 \

? *(p)->ptr++ = (x) : _flushbuf((x),p))

 

#define getchar() getc(stdin)

#define putcher(x) putc((x), stdout)

Макрос getc, як правило, зменшує відлік, просуває покажчик і повертає знак. (Якщо пам'ятаєте, довший рядок #define можна перенести за допомогою зворотньої похилої.) Проте, якщо відлік виявиться від'ємним, getc викликає функцію _fillbuf для дозаповнення буфера, ініціювання наново змісту структури і повернення знака. Символи повертаються як беззнакові, щоб упевнитись, що усі вони додатні.

Хоч ми не обговорюватимемо деталей, ми включили також визначення putc, щоб показати, що вона діє подібно до getc, викликаючи функцію _flushbuf, коли її буфер заповнено. Ми включили так само макрос визначення статусу помилки і кінця файла і дескриптор.

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

#include <fcntl.h>

#include "syscalls.h"

#define PERMS 0666 /* RW for owner, group, others */

 

FILE *fopen(char *name, char *mode)

{

int fd;

FILE *fp;

 

if (*mode != 'r' && *mode != 'w' && *mode != 'a')

return NULL;

for (fp = _iob; fp < _iob + OPEN_MAX; fp++)

if ((fp->flag & (_READ | _WRITE)) == 0)

break; /* found free slot */

if (fp >= _iob + OPEN_MAX) /* no free slots */

return NULL;

 

if (*mode == 'w')

fd = creat(name, PERMS);

else if (*mode == 'a') {

if ((fd = open(name, O_WRONLY, 0)) == -1)

fd = creat(name, PERMS);

lseek(fd, 0L, 2);

} else

fd = open(name, O_RDONLY, 0);

if (fd == -1) /* couldn't access name */

return NULL;

fp->fd = fd;

fp->cnt = 0;

fp->base = NULL;

fp->flag = (*mode == 'r') ? _READ : _WRITE;

return fp;

}

Ця версія fopen не оперує всіма режимами доступу, зазначених стандартом, але додання їх не займе багато коду. Зокрема, наша fopen не розпізнає «b», що позначає бінарний доступ, оскільки це не має змісту на системах UNIX, так само «+», що дозволяє одночасно читання і запис. Перший виклик getc для певного файла зіткнеться з відліком рівним нулю, що спричинить виклик _fillbuf. Якщо _fillbuf виявить, що файл недоступний для читання, вона негайно поверне EOF. У протилежному випадку, вона спробує виділити буфер (якщо читання буфероване).

Як тільки започатковано буфер, _fillbuf викличе read, щоб заповнити його, встановить лічильник і покажчики, і поверне перший символ буфера. Наступні виклики _fillbuf матимуть виділений буфер.

#include "syscalls.h"

 

/* _fillbuf: allocate and fill input buffer */

int _fillbuf(FILE *fp)

{

int bufsize;

 

if ((fp->flag&(_READ|_EOF_ERR)) != _READ)

return EOF;

bufsize = (fp->flag & _UNBUF) ? 1 : BUFSIZ;

if (fp->base == NULL) /* no buffer yet */

if ((fp->base = (char *) malloc(bufsize)) == NULL)

return EOF; /* can't get buffer */

fp->ptr = fp->base;

fp->cnt = read(fp->fd, fp->ptr, bufsize);

if (--fp->cnt < 0) {

if (fp->cnt == -1)

fp->flag |= _EOF;

else

fp->flag |= _ERR;

fp->cnt = 0;

return EOF;

}

 

return (unsigned char) *fp->ptr++;

}

Єдиним не з'ясованим питанням є, як все розпочати. Потрібно спершу означити і ініціювати масив _iob для stdin, stdout і stderr:

FILE _iob[OPEN_MAX] = { /* stdin, stdout, stderr */

{ 0, (char *) 0, (char *) 0, _READ, 0 },

{ 0, (char *) 0, (char *) 0, _WRITE, 1 },

{ 0, (char *) 0, (char *) 0, _WRITE, | _UNBUF, 2 }

};

Ініціалізація прапорців структури показує нам, що stdin читатиметься, stdoutзаписуватиметься і stderr записуватиметься небуферовано.

Вправа 8-2. Перепишіть fopen і _fillbuf з полями замість явних операцій з бітами. Порівняйте розмір коду і швидкість виконання.

Вправа 8-3. Розробіть і напишіть _flushbuf, fflush і fclose.

Вправа 8-4. Функція стандартної бібліотеки

int fseek(FILE *fp, long offset, int origin)

є ідентичною lseek за винятком того, що fp являється покажчиком на файл замість дескриптора і повернене значення являється статусом типу int, а не позицією. Напишіть fseek. Впевніться, що ваша fseek відповідно узгоджується з буферуванням, що має місце в решті функцій бібліотеки.


<== попередня лекція | наступна лекція ==>
Довільний доступ - lseek | Приклад - перелік вмісту каталогів


Онлайн система числення Калькулятор онлайн звичайний Науковий калькулятор онлайн