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

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

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

362

 

 

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

perrorCint.dat") ;

return

1;

 

}

(i=l;

i<ll;

i++)

for

{

fscanf(fp,

"%d %d", in, inn);

 

}

printf(" %d %d \n",n, nn);

 

 

 

fclose(fp);

 

 

return

0;

 

 

>

 

 

 

Позиционирование в потоке. В начале главы были рас­ смотрены посимвольный, построчный й форматный обмены с файлами, организованными в виде потока байтов. Эти средства позволяли записать в файл данные и читать из него информа­ цию только последовательно.

Операция чтения (или записи) для потока всегда производит­ ся, начиная с текущей позиции в потоке. Начальная позиция чтения/записи в потоке устанавливается при открытии потока и может соответствовать начальному или конечному байту потока в зависимости от режима открытия потока. При открытии пото­ ка в режимах "г" и "w" указатель текущей позиции чте­ ния/записи в потоке устанавливается на начальный байт потока, а при открытии в режиме "а" - в конец файла (за конечным бай­ том). При выполнении каждой операции ввода-вывода указатель текущей позиции в потоке перемещается на новую текущую позицию в соответствии с числом прочитанных (записанных) байтов.

Рассмотрим средства позиционирования в потоке, позво­ ляющие перемещать указатель (индикатор) текущей позиции в потоке на нужный байт. Эти средства дают возможность рабо­ тать с файлом на диске, как с обычным массивом, осуществляя доступ к содержимому файла в произвольном порядке.

В библиотеку языка Си включена функция fseek() для пере­ мещения (установки) указателя текущей позиции в потоке на нужный байт потока (файла). Она имеет следующий прототип:

int fseek {указатель на поток, смещение, начало отсчета)-,

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

363

Смещение задается переменной или выражением типа long и может быть отрицательным, т.е. возможно перемещение по файлу в прямом и обратном направлениях. Начало отсчета зада­ ется одной из предопределенных констант, размещенных в за­ головочном файле stdio.h:

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

Здесь следует напомнить некоторые особенности данных ти­ па long. Этот тип определяется для целочисленных констант и переменных, которым в памяти должно быть выделено больше места, чем данным типа int. Обычно переменной типа long вы­ деляется 4 байта, чем и определен диапазон ее значений. Опи­ сание данных типа long:

long А, р, Z [16];

Константа типа long записывается в виде последовательно­ сти десятичных цифр, вслед за которыми добавляется раздели­ тель L или 1. Примеры констант типа long:

01

0L

10L

688L

331

Втекстах программ лучше использовать L, чтобы не путать 1

сцифрой 1.

Функция fseek() возвращает 0, если перемещение в потоке (файле) выполнено успешно, в противном случае возвращается ненулевое значение.

Приведем примеры использования функции fseek(). Пере­ мещение к началу потока (файла) из произвольной позиции:

fseek(fp, 0L, SEEK_SET);

Перемещение к концу потока (файла) из произвольной пози­ ции:

fseek(fp, 0L, SEEK_END);

В обоих примерах смещение задано явно в виде нулевой де­ сятичной константы 0L типа long.

364

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

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

struct str {• ■•

} st;

Тогда при следующем обращении к функции fseek() указа­ тель текущей позиции в потоке будет перемещен на одну струк­ туру назад относительно текущей позиции:

fseek(fp, - (long)sizeof(st), SEEK_CUR);

Для иллюстрации работы с файлом в режиме произвольного доступа рассмотрим пример небольшого трехъязычного слова­ ря, содержащего названия цифр от 1 до 9 на английском, немец­ ком и французском языках. Для обслуживания словаря необ­ ходимы 2 программы: программа загрузки словаря в файл на диске и программа поиска слова в словаре (в ней используется функция fseek()).

"База данных" словаря организована в файле vac.dat. Функ­ ция загрузки запрашивает названия для цифр от 1 до 9. Назва­ ния конкретной цифры (в предположении, что каждое название не превышает 9 букв) на всех трех языках записываются в од­ ной строке через пробелы. Сформированная строка по нажатию клавиши <Enter> прочитывается в массив символов buf[ ]. Текст программы:

#±nclude <stdio.h> int main ()

{

int i , k ;

 

int

n;

/* Буфер */

char buf[30];

char

*c ;

 

int

*pti;

 

FILE *fp; /* Указатель на поток */ /* Открыть файл для записи */

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

365

if ((fp =

fopen("vac.dat","w")) == NULL)

perror("vac.dat");

return

1;

 

 

}

(i=l;

i<=9; i++)

for

{ /*

Очистить

буфер: */

for

(k=0;

k<30; k++)

 

buf[k]

=

' ';

/* Запрос названий цифр */

n =

i;

 

 

 

pti

= &n;

 

 

printf("\n Введите названия цифры %d:\n”, i) ;

scanf("%s

%s

%s", buf, buf+10, buf+20);

/* Запись в файл цифры */

с = (char

*)pti;

for (k=0;

к <2;

k++)

putc(*c++,

fp) ;

/* Запись

в файл названий цифр */

с = buf;

к<30;

к++)

for (к=0;

putc(*c++,

fp);

fclose(fp); return 0;

Введенные данные (представление цифры в формате целого числа и названия цифр на трех языках) побайтно выводятся в файл vac.dat, образуя в нем "записи", структура которых показа­ на на рис. 7.2.

2 байта

10 байт

10 байт

10 байт

Цифра

Названия цифр на разных языках

Рис. 7.2. Состав записи "Цифра1

366

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

Следующая программа перевода (trans) запрашивает сначала язык, на котором нужно выводить названия цифр, а затем в цик­ ле запрашиваются цифры. Язык идентифицируется первой бук­ вой своего русского наименования: 'а' или 'А' - для английского; 'н' или 'Н' - для немецкого; 'ф' или 'Ф' - для французского. При­ знаком завершения программы служит ввод цифры 0. Текст программы trans:

/* Программа перевода trans.с */ #±nclude <stdio.h>

int main ()

{

int i , k ; char *c ;

long pos; /* Смещение в файле */ char buf[30];

char In;•

FILE *fp; /* Указатель на поток */ int lang; /* Индикатор "язык" */ /* Открыть файл: */

if ((fp = fopen("vac.dat","r")) == NULL)

{

perror("vac.dat"); return 1;

}

/* Запросить язык: */ lang = -1;

while (lang == -1)

{

puts("\n Введите первую букву названия" " языка: (а,н,ф)");

scanf("%c"?l &1п) ;

if <(In ==''а ') |I(In = ' A ' ) ) lang = 0; else

if ((In = = ' H ' ) ||(In =='H')) lang = 1; else

if ((In =='ф ’)||(In = ' * ’)) lang = 2 ;

}

while (1) /* Цикл перевода */

{

с = buf;

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

367

puts("введите цифру (0 - для завершения):"); scanf("%d", &к) ;

if (к = = 0)

{

fclose(fp); return 0;

}

/* Вычислить смещение */ pos = (к—1)*32+2+lang*10; fseek(fp, pos, SEEK_SET); /* Выбрать перевод */

for (i=0; i<=9; i++) *c++=getc(fp);

C + + ;

*c = '\0'; printf("%d->%s\n", k, buf);

}

fclose(fp); return 0;

}

При вычислении позиции (pos) в файле, с которой начинает­ ся строка перевода, участвуют следующие составляющие:

к-1 - выбирает подстроку (длиной 32 байта), в которой со­ держится перевод;

2 - учитывает длину поля цифры (2 байта);

lang* 10 - задает смещение до требуемого значения цифры в подстроке перевода.

На рис. 7.3 показаны составляющие смещения для искомого поля с переводом на немецкий язык.

______________

к-й

2 байта

10 байт

10 байт

10 байт

элемент

к

 

 

 

(к-1

*32

 

 

 

Рис. 7.3. Составляющие смещения для записей в файле словаря

368

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

Предлагаем самостоятельно разработать функцию поиска в файле заданной подстроки и на ее основе написать программу перевода названий цифр с одного языка на другой. В табл. 7.3 приводятся названия цифр от 1до 9 на трех языках.

 

 

 

Таблица 7.3

 

Трехъязычный словарь "Цифры"

 

Английский

Немецкий

Французский

язык

язык

язык

 

1

ONE

EINS

UN

2

TWO

ZWEI

DEUX

3

THREE

DREI

TROIS

4

FOUR

VIER

QUATRE

5

FIVE

FUNF

CINQ

6

SIX

SECHS

SIX

7

SEVEN

SIEBEN

SEPT

8

EIGHT

ACHT

HUIT

9 .

NINE

NEUN

NEUF

Кроме рассмотренной функции fseek(), в библиотеке функ­ ций языка Си находятся следующие функции для работы с ука­ зателями текущей позиции в потоке:

long ftell(FILE *) - получить значение указателя текущей по­ зиции в потоке;

void rewind(FILE *) - установить указатель текущей позиции в потоке на начало потока.

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

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

369

7.2. В вод-вы вод ниж него уровн я

 

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

Функции ввода-вывода более низкого уровня позволяют пользоваться средствами ввода-вывода операционной системы непосредственно. При этом не выполняются буферизация и фор­ матирование данных. Программы, использующие низкоуровне­ вый ввод-вывод, переносимы в рамках некоторых систем про­ граммирования Си, в частности, относящихся к UNIX. Учитывая близость функций низкоуровневого- ввода-вывода к средствам ввода-вывода операционной системы, можно реко­ мендовать их применение для разработки собственной подсис­ темы ввода-вывода, например ориентированной на работу со сложными структурами данных (списки, деревья, сложные за­ писи и т.п.).

При низкоуровневом открытии файла с ним связывается не указатель файла (потока), а дескриптор (handle) файла. Деск­ риптор является целым значением, характеризующим размеще­ ние информации об открытии файла во внутренних таблицах операционной системы. Дескриптор файла используется при последующих операциях с файлом.

В библиотеку языка Си включены следующие основные функции ввода-вывода нижнего уровня:

open( )/cIose() - открыть / закрыть файл;

creat( ) - создать файл;

read( )/w rite() - читать / писать данные;

sopen() - открыть файл в режиме разделения, т.е. для од­ новременного доступа со стороны нескольких процессов (работающих программ);

eof() - проверить достижение конца файла;

Iseek( ) - изменить текущую позицию в файле;

tell() - получить значение текущей позиции в файле.

24 -3 1 2 4

370

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

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

Функции нижнего уровня в отличие от функций для работы с потоком не требуют включения в программу заголовочного файла stdio.h. Однако, этот файл содержит определения ряда констант (например, признак конца файла EOF), которые могут оказаться полезными. В случае применения этих констант файл stdio.h должен быть включен в программу.

7.2.1. Открытие/закрытие файла

До выполнения операций ввода-вывода в файл (из файла) на низком уровне необходимо открыть или создать файл одной из следующих функций: open(), sopen() или creat().

Функция sopen() используется в том случае, ко(да необхо­ димо дать возможность одновременного доступа к файлу для нескольких выполняющихся программ. Разумеется, речь идет о доступе к файлу в режиме чтения. Обычно файл блокируется для доступа со стороны других выполняющихся программ, и именно функция sopen() необходима для разрешения одновре­ менного доступа.

При открытии файла в программу возвращается дескриптор файла, значение которого является целочисленным. В отличие от дескриптора указатель на поток есть указатель на структуру типа FILE, определенного в заголовочном файле stdio.h.

Формат вызова функции ореп(), в результате выполнения которой приобретает значение дескриптор файла:

fd = open ( имя файла, флаги, права доступа );

0_APPEND
0_BINARY
0_CREAT
О EXCL
0_RD0NLY
0_RDWR
0_TEXT
0_TRUNC

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

•371

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

Второй параметр флаги определяет режим открытия файла, который является выражением, сформированным (с помощью —побитовой операции ИЛИ) из одной или более предопреде­ ленных конетант, размещенных в заголовочном файле fcntl.hr В некоторых реализациях UNIX эти константы находятся в файле sys/file.h.

Примечание. Обратите внимание иа то, что в UNIX при об­ разовании полного имени файла применяется символ 7 (прямой слэш), а не обратный слэш ('V), как в MS-DOS.

Приведем в алфавитном порядке список констант, задающих режим открытия файла, с кратким описанием их назначения:

- открыть файл для добавления (для записи в конец файла);

- открыть файл в бинарном режиме (см. §7.1.1);

- создать и открыть новый файл;

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

- открыть файл только для чтения; - открыть файл и для чтения, и для записи;

- открыть файл в текстовом режиме (см. §7.1.1);

- открыть существующий файл и стереть его содержимое (подготовить для записи новой информации).

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

24*

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