Шрифт:
4.4.3. Чтение и запись
Ввод/вывод осуществляется системными вызовами
read
и write
соответственно:
#include <sys/types.h> /* POSIX */
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
Каждая функция сделана как можно проще. Аргументами являются дескриптор открытого файла, указатель на буфер для чтения или записи данных и число читаемых или записываемых байтов.
Возвращаемое значение является числом действительно прочитанных или записанных байтов. (Это число может быть меньше запрошенного: при операции чтения это происходит, когда в файле осталось меньше
count
байтов, а при операции записи это случается, когда диск заполнен или произошла еще какая-нибудь ошибка.) Возвращаемое значение -1 означает возникшую ошибку, в этом случае errno указывает эту ошибку. Когда read
возвращает 0, это означает, что достигнут конец файла. Теперь мы можем показать оставшуюся часть кода для
ch04-cat
. Процедура process
использует 0 для стандартного ввода, если именем файла является «–
» (строки 50 и 51). В противном случае она открывает данный файл:
36 /*
37 * process --- сделать что-то с файлом, в данном случае,
38 * послать его в stdout (fd 1).
39 * Возвращает 0, если все нормально; в противном случае 1.
40 */
41
42 int
43 process(char *file)
44 {
45 int fd;
46 ssize_t rcount, wcount;
47 char buffer[BUFSIZ];
48 int errors = 0;
49
50 if (strcmp(file, "-") == 0)
51 fd = 0;
52 else if ((fd = open(file, O_RDONLY)) < 0) {
53 fprintf(stderr, "%s: %s: cannot open for reading: %s\n",
54 myname, file, strerror(errno));
55 return 1;
56 }
Буфер
buffer
(строка 47) имеет размер BUFSIZ
; эта константа определена В <stdio.h>
как «оптимальный» размер блока для ввода/вывода. Хотя значение BUFSIZ
различается в разных системах, код, использующий эту константу, чистый и переносимый. Основой процедуры является следующий цикл, который повторно читает данные до тех пор, пока не будет достигнут конец файла или не возникнет ошибка.
58 while ((rcount = read(fd, buffer, sizeof buffer)) > 0) {
59 wcount = write(1, buffer, rcount);
60 if (wcount != rcount) {
61 fprintf(stderr, "%s: %s: write error: %s\n",
62 myname, file, strerror(errno));
63 errors++;
64 break;
65 }
66 }
Переменные
rcount
и wcount
(строка 45) имеют тип ssize_t
, «знаковый size_t
», который позволяет хранить в них отрицательные значения. Обратите внимание, что число байтов, переданное write
, является значением, возвращенным read
(строка 59). Хотя мы хотим читать порциями фиксированного размера в BUFSIZ
, маловероятно, что размер самого файла кратен BUFSIZ
. При чтении из файла завершающей, меньшей порции байтов, возвращаемое значение указывает, сколько байтов buffer получили новые данные. В стандартный вывод должны быть скопированы только эти байты, а не весь буфер целиком. Условие '
wcount != rcount
' в строке 60 является правильным способом проверки на ошибки; если были записаны некоторые, но не все данные, wcount
будет больше нуля, но меньше rcount
. В заключение
process
проверяет наличие ошибок чтения (строки 68–72), а затем пытается закрыть файл. В случае (маловероятном) неудачного завершения close
(строка 75) она выводит сообщение об ошибке. Избежание закрытия стандартного ввода не является абсолютно необходимым в данной программе, но является хорошей привычкой при разработке больших программ, в случае, когда другой код где-то в другом месте хочет что-то с ним делать или если порожденная программа будет наследовать его. Последний оператор (строка 82) возвращает 1, если были ошибки, и 0 в противном случае.