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

книги / Программирование на языке Си

..pdf
Скачиваний:
15
Добавлен:
12.11.2023
Размер:
17.16 Mб
Скачать

372

Программирование на языке Си

Третий параметр - права доступа должен применяться толь­ ко в режиме открытия файла 0_CREAT, т.е. только при созда­ нии нового файла.

В операционных системах MS-DOS и Windows для задания параметра правадоступа используются следующие предопре­ деленные константы:

S_IWRITE - разрешить запись в файл;

S_IREAD - разрешить чтение из файла;

S_IREAD|S_IWRITE - разрешить и чтение, и запись (совпа­ дает с SJWRITE).

Перечисленные константы размещены в заголовочном файле stat.h, находящемся в каталоге sys системы программирования Си. Обычно его подключение осуществляется директивой #include <sys\stat.h>.

Если параметр права_доступа не указан, то устанавливается разрешение только на чтение из файла. Чаще всего в операци­ онных системах MS-DOS и Windows этот параметр не исполь­ зуется.

В UNIX в силу того, то она является многопользовательской, система защиты файлов более развита. Права доступа к файлам устанавливаются для трех категорий пользователей:

владелец файла;

участник группы пользователей;

прочие пользователи.

Права доступа к конкретному файлу устанавливаются вла­ дельцем файла специальными командами. Права доступа ото­ бражаются при просмотре оглавления каталога командой Is -I в виде символьной строки, которая формируется по следующему правилу: для каждой группы пользователей в строке прав дос­ тупа выделяется 3 символа, каждый из которых может прини­ мать следующие значения:,

г - разрешено чтение из файла; w - разрешена запись в файл;

х- разрешено выполнение файла (для файлов, хранящих исполняемую программу).

Глава 7. Ввод и вывод

373

Символы г, w, х задаются строго на своих местах в указан­ ном порядке (rwx). Если какой-либо из типов доступа к файлу запрещен, на месте соответствующего символа записывается символ (минус). Таким образом, если для владельца файла разрешены все виды доступа к файлу (rwx), для участника группы пользователей - только чтение и выполнение (r-х), а для прочих пользователей - только выполнение (~х), то строка прав доступа будет выглядеть так:

rwxr-x— X

От этой строки символов легко перейти к собственно пара­ метру правадоступа, являющемуся целым числом. Если на соответствующем месте в строке указан символ, отличный от то записывают Т , иначе - записывают '0'. Получившееся двоич­ ное число (111101001) переводят в восьмеричное, записав в ви­ де восьмеричной цифры каждую группу из трех двоичных цифр, начиная с самой правой группы: 0751. Это число и следует ука­ зать в качестве параметра права доступа в функции ореп().

Приведем несколько примеров открытия файла. 1. Открыть файл для чтения:

fd = open("t.txt", 0_RD0NLY);

2. Открыть существующий файл для записи новых данных:

fd = open("new.txt", OJWRONLY|0_CREAT|0_TRUNC,0600);

Параметр права доступа, заданный восьмеричной константой 0600 (для UNIX), в символьном изображении имеет вид rw------, т.е. для владельца файла разрешены чтение и запись, для двух других категорий пользователей не разрешен ни один из видов доступа к файлу. Если файл с именем new.txt существует, то перед записью новых данных он будет усечен до нулевого раз­ мера (очищен). При открытии файла с указанными параметрами в других ОС параметр права доступа опускается.

3. Открыть файл для добавления:

fd=open("t.txt",

О WRONLY|О APPEND|О CREAT,0600);

374

Программирование на языке Си

4.Открыть файл для чтения и записи: fd = open("t.txt", 0_RDWR);

5.Создать новый файл для записи:

if ((fd = open("tmpfile", 0_WR0NLY|0_CREAT| 0_EXCL, 0666)) == -1)

puts("tmpfile уже существует\п");

В операционной системе UNIX такая последовательность опе­ раторов открывает новый файл для записи. Если файл не суще­ ствует, то он создается. Иначе функция завершается неудачей. Флаг O EXCL специально задан для предотвращения непредна­ меренного уничтожения уже существующего файла. Этот флаг используется совместно с O CREAT. Права доступа (rw-rw-rw-) разрешают чтение и запись в файл для всех категорий пользова­ телей. В других ОС параметр права доступа должен быть опу­ щен.

Приведем более полный пример создания файла (MS-DOS,

Windows):

#include <stdio.h> #include <stdlib.h> #include <io.h> #include <errno.h> #include <fcntl.h> #include <sys\stat.h> void main( )

{

int fd; /* Дескриптор файла */ if ((fd = open("tmpfile",

0_RDWR|0_CREAT|0_EXCL,

S_IREAD |S_IWRITE)) < 0)

{.

if (errno == EEXIST) fprintf(stderr, "файл tempfile"

" уже' существует\п") ;

exit(errno);

}

}

Глава 7. Ввод и вывод

375

Создаваемый файл в соответствии с выбранными флагами открывается для чтения и записи. Права доступа (rw-------) по­ зволяют только владельцу работать с файлом (читать и писать).

Для идентификации ошибок, возникающих при открытии файла, используется именующее выражение (переменная) errno, определенное в заголовочном файле errno.h. При выпол­ нении функций стандартной библиотеки в область памяти, име­ нуемой errno, записываются коды ошибок. Предопределенная в errno.h константа EEXIST означает, что файл, указанный в функции ореп( ), уже существует. В этом примере для вывода сообщения об ошибке применена функция форматного вывода в файл fprintf(), в которой использован предопределенный деск­ риптор файла stderr стандартного потока для вывода сообще­ ний об ошибках. ' {

Кроме функции ореп( ), для открытия файла можно исполь­ зовать функцию creat(), упомянутую в начале параграфа. Функция creat( ) полностью эквивалентна такому вызову функ­ ции ореп():

open (имя файла, 0_CREAT|0_TRUNC|0_WR0NLY);

Функция creat() создает новый файл и открывает его для за­ писи. Наличие в библиотеке наряду с функцией ореп( ) функции creat() вызвано требованиями совместимости с ранними вер­ сиями UNIX, имевшими только три основных режима открытия файла (O RDONLY, O WRONLY, 0_RDWR), что вынуждало использовать для создания нового файла специальную функцию creat().

Так же как и при использовании потоков, в начале работы каждой программы автоматически открываются файлы стан­ дартного ввода, стандартного вывода и стандартного вывода сообщений об ошибках. Эти файлы имеют значения дескрипто­ ров файлов 0, 1 и 2, которые можно использовать при обменах на нижнем уровне со стандартными файлами.

Необходимо иметь в виду, что в каждой операционной сис­ теме имеется ограничение на количество одновременно откры­ тых в программе файлов. Обычно их число устанавливается от

376

Программирование на языке Си

20 до 40. Во время работы программы, в которой обрабатывает­ ся большое количество файлов, необходимо своевременно за­ крывать ненужные файлы. Для закрытия файла на нижнем уровне служит функция close(), прототип которой имеет вид:

int close {дескриптор_фаша)\

Функция cIose() при успешном завершении возвращает 0. В случае ошибки возвращается -1.

При завершении программы все открытые файлы автомати­ чески закрываются.

7.2.2. Ч тение и запись данны х

Ввод-вывод данных на нижнем уровне осуществляется функциями read() и write( ). Прототипы этих функций имеют следующий вид:

int read(int fd, char *buffer, unsigned int count);

int write(int fd, char *buffer, unsigned int count);

Обе функции возвращают целое число - количество действи­ тельно прочитанных или записанных байтов,.

Функция read( ) читает количество байтов, заданное третьим параметром count, из файла, открытого с дескриптором файла fd, в буфер, определенный указателем buffer. При достижении конца файла функция read() возвращает значение 0. В случае возникновения ошибки при чтении из файла функция read() возвращает значение -1.

Операция чтения, так же как и для потокового ввода-вывода, начинается с текущей позиции в файле. После завершения опе­ рации чтения текущая позиция будет определять первый непро­ читанный символ.

Если файл открыт в текстовом режиме, то происходят точно такие же преобразования при вводе последовательности симво­ лов CR и LF в символ '\n' (LF), как и при работе с потоком. Ука­ занное преобразование приводит к тому, что в возвращаемом

Глава 7. Ввод и вывод

377

значении вместо двух символов CR и LF учитывается только один символ '\n' (LF).

Функция write( ) записывает последовательность байтов, ко­ личество которых задано третьим параметром count, в файл, ''от­ крытый с дескриптором файла fd, из буфера, определенного указателем buffer. Запись производится с текущей позиции. Ес­ ли файл открыт в текстовом режиме, то количество реально за­ писанных байтов может превышать count за счет преобразова­ ний всех символов 'V в последовательности символов CR, LF. Таким образом, count будет содержать только количество сим­ волов, взятых из буфера.

Если при выполнении операции записи возникла ошибка, то функция w rite() возвращает значение -1, а глобальная пере­ менная errno получает одно из следующих значений, заданных предопределенными константами в заголовочном файле errno.h:

EACCES - файл защищен для записи (доступен только для чтения);

ENOSPC - исчерпано свободное пространство на внешнем устройстве;

EBADF - недействительный дескриптор файла.

Приведем два примера применения функций низкоуровнево­ го ввода-вывода.

П рим ер 1.

Копирование последовательности отдельных символов iA стандартного ввода в стандартный вывод:

#include <io.h> int main( )

{

char c[2];

while ((read(0, c, 1)) > 0) write(1, c, 1);

return 0 ;

}

В текст программы включается заголовочный файл io.h, со­ держащий прототипы функций read() и w rite(). При вызове

378 Программирование на языке Си

этих функций для файлов стандартного ввода и стандартного вывода используются соответственно значения дескрипторов стандартных файлов 0 и 1. Прочитанный символ и код клавиши <Enter>, который служит признаком завершения набора вводи­ мой последовательности символов, записываются в одномерный массив с[ ] из 2 байтов, откуда они затем функцией w rite() вы­ водятся на экран дисплея.

Запустив программу на выполнение, можно вводить одиноч­ ные символы с клавиатуры, завершая ввод каждого из них на­ жатием на клавишу <Enter>. Результат работы программы может выглядеть так:

V

V

w w

в

е

<Ctrl+C>

Первый символ из пары одинаковых символов (v-v; w-w и т.д.) - это символ, введенный с клавиатуры и выведенный сис­ темой ввода-вывода на экран (стандартный режим ввода данных с клавиатуры). Второй символ пары выведен на устройство стандартного вывода функцией write(). Программа копирова­ ния завершает работу при вводе сигнала прерывания (одновре­ менном нажатии клавиш <Ctrl> и <С>}.-

П р и м ер 2.

Копирование произвольного файла

Программа получает имена файлов из командной строки при запуске и позволяет копировать произвольные файлы, (см. ана­ лог этой программы в §7.1.3).

#include <stdio.h> #include <fcntl.h> #include <io.h>

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

{

int fdin, fdout; /* Дескрипторы файлов */;

Глава 7. Ввод и вывод

 

 

379

int п; /* Количество прочитанных байтов */

char buff[BUFSIZ];

 

 

if ( argc != 3)

 

 

{

 

 

 

printf("Формат вызова программы:");

 

printf("\n

%s файл_источник файл_приемник",

argv[0]) ;

 

 

return 1;

 

 

 

>

open(argv[l], 0_RD0NLY)) ==

-1)

if ((fdin =

{

 

 

 

perror(argv[1]);

 

 

return 1;

 

 

 

}

open(argv[2],

 

if ((fdout =

 

0_WR0NLY|0_CREAT|0_TRUNC)) == -1)

 

(

 

 

 

perror(argv[2]);

 

 

return 1;

 

 

 

}

 

 

 

/* Файлы открыты - можно копировать */

> 0 )

while ((n =

read(fdin,

buff, BUFSIZ))

write(fdout, buff,

n);

 

return 0;

 

 

 

}

 

 

 

Константа BUFSIZ (размер буфера для потокового вводавывода) определена в заголовочном файле stdio.h. Ее значение для MS-DOS равно 512 байт.

Для применения этой программы в ОС MS-DOS необходимо вначале построить исполняемую программу (см. гл. 9), напри­ мер, copyf.exe. Вызов программы из командной строки будет иметь вид:

>copyf f1.dat

f2 .dat

где f1.dat - файл-источник; f2.dat - файл-приемник.

Если ошибок при исполнении программы нет, файл будет скопирован, но никаких сообщений на экране дисплея не поя­ вится..

380

Программирование на языке Си

7.2.3.Произвольный доступ к файлу

Вприведенных выше программах с функциями ввода-вывода

,низкого уровня обмен с файлом осуществлялся последователь­ но. При необходимости файл можно читать на низком уровне и

впроизвольном порядке. Так же, как это делалось при работе с потоками, можно изменять значение указателя текущей позиции чтения/записи в файле. Для этой цели служит функция Iseek( ). Прототип этой функции имеет следующий вид:

long (seek (int fd, long offset, int origin);

Функция lseek() изменяет текущую позицию в файле, свя­ занном с дескриптором fd, на новую, определяемую смещением (второй параметр - offset) относительно выбранной Точки от­ счета (третий параметр - origin). .

Точка отсчета задается одной из предопределенных кон­ стант, размещенных в заголовочном файле io.h (MS-DOS) или файле unistd.h (UNIX):

SEEK_SET (имеет значение 0) - начало файла; SEEK_CUR (имеетзначение 1)-текущая позиция; SEEK_END (имеет значение 2) - конец файла.

При удачном завершении функция lseek() возвращает новую текущую позицию чтения/записи, представляющую собой сме­ щение от начала файла. Попытка переместиться за пределы файла считается ошибкой. Код ошибки заносится в глобальную Переменную еггпо, определенную в заголовочном файле errno.h.

Для определения текущей позиции в файле можно использо­ вать функцию tell( ), прототип которой имеет следующий вид:

long tell (int fd);

Приведем примеры использования функции lseek( ).

Пр имер 1.

Установка текущей позиции в файле на его начало:

lseek(fd, OL, SEEKJSET);

Глава 7. Ввод и вывод

381

П р и м ер 2.

Установка текущей позиции для последующего добавле­ ния данных в файл (позиция в конце файла):

lseek(fd, OL, SEEK_END);

П рим ер 3.

Модификация записей в существующем файле

В качестве еще одного примера использования функции Iseek() приведем фрагмент программы, производящей модифи­ кацию записей в существующем файле. Запись - это объект (последовательность байтов) в файле, являющаяся сложным структурным элементом в контексте программы, обрабатываю­ щей эти записи. Логическая структура записи может быть лю­ бой: некоторой последовательностью байтов, строкой, структурой и т.д. В следующем фрагменте программы предпо­ лагается, что все записи имеют одинаковый размер (в байтах) и размещены в файле подряд.

/* Прочитать запись в буфер */ read(fd, buff, sizeof ( запись, ));

/* Вернуть указатель в файле на место, с которого начиналось чтение записи */

lseek(fd, -sizeof( запись ), SEEK_CUR); /* Откорректировать запись в буфере */

/* Поместить запись на прежнее место */ write(fd, buff, sizeof( запись ));

Буфер buff может быть определен как массив символов, дос­ таточный для размещения одной записи.

Соседние файлы в папке книги