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

7_ Файлы .

...doc
Скачиваний:
8
Добавлен:
10.02.2015
Размер:
192.4 Кб
Скачать

6 ФАЙЛЫ И ИХ ПРИМЕНЕНИЕ

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

6.1 Дисковые запоминающие устройства

Диск. Поверхность, дорожка, сектор. Файл и его имя. Каталоги. Полное имя файла. Вывод и ввод. Время поиска и время ожидание. Скорость вращения.

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

Использование магнитного диска в качестве носителя информации требует выполнения определенных подготовительных операций, называемых физическим форматированием или разметкой диска. В ходе форматирования каждой поверхности диска присваивается индивидуальный номер. Поверхности делятся на концентрические окружности (дорожки). Каждая дорожка получает индивидуальный номер и разбивается на части, называемые секторами. Секторы также нумеруются. Информации на диске хранится в двоичном виде. Обычно в каждый сектор можно поместить (записать) 512 байт пользовательской информации.

Файлом называется именованная последовательность байтов диска, в которых хранится информация. Количество содержащихся в файле байтов называется его длиной. Операционные системы семейства Windows запрещают использовать в именах файлов следующие девять символов: \, /, |, :, ?, “, *, <, >. К имени файла разрешается добавлять начинающийся с точки набор символов, который называется расширением имени. Файлы можно объединять в группы по тому или иному признаку. Список каждой группы хранится в отдельном файле, который принято называть каталогом или папкой. Файлы и каталоги, не входящие ни в какие другие каталоги, образуют каталог нулевого уровня, главный или корневой каталог диска. Файлы и каталоги, хранящиеся в каталогах нулевого уровня, образуют первый уровень. А файлы и каталоги, хранящиеся в каталогах -ого уровня образуют уровень. Путем к файлу называется последовательность, состоящая из имени дискового устройства и имен каталогов, содержащих этот файл. Имена перечисляются, начиная с каталога нулевого уровня, и разделяются обратными косыми чертами \. В качестве примера ниже указаны пути к файлам и каталогам нулевого уровня диска С:, к файлам и каталогам первого уровня, содержащимися в каталоге нулевого уровня с именем WINDOWS (они являются подкаталогами WINDOWS), а также к файлам и каталогам второго уровня из каталога первого уровня с именем system32:

С:\

С:\WINDOWS\

С:\WINDOWS\system32\

Путь к файлу и его имя называются полным именем файла. Для доступа к файлу необходимо использовать его полное имя.

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

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

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

Перед чтением необходимо переместить магнитную головку на нужную дорожку. Эта операция называется позиционированием, а время ее выполнения – временем поиска (seek time). Затем требуется дождаться, пока начало нужного сектора подойдет к головке. Время, которое требуется для этого, называется временем ожидания (latency). Обычно указывают время, за которое диск делает половину оборота. Его называют средним временем ожидания. Для снижения времени ожидания производители дисковых устройств постоянно увеличивают угловую скорость вращения шпинделя. Когда-то она равнялась 3600 оборотам в минуту. Потом ее подняли до 5400, а затем и до 7200 об/мин. В настоящее время она составляет для большинства дисковых устройств 9000 об/мин. При такой скорости вращения среднее время ожидания составляет около 3 мс.

6.2 Типы и форматы файлов

Текстовые файлы. Графические файлы. Файлы с приложениям. Форматы файлов.

В зависимости от хранящейся в файле информации его можно отнести к одному из нескольких типов. Файлы, в которых хранится текст, называются текстовым. Такие файлы создаются, как правило, при помощи клавиатуры и соответствующих программных средств, которые называются текстовыми редакторами. В первых текстовых файлах каждый символ (буква, цифра, знак препинания и другие) представлялся своим ASCII-кодом и занимал один байт. После каждой строки требовалось помещать пару символов: ‘\r‘ - возврат каретки и ‘\n‘ - переход на новую строку. Текстовые файлы, отвечающие этим требованиям, принято называть ASCII-фай­лами. К их именам часто добавляют расширение .asc. Современные текстовые файлы помимо ASCII-кода символа содержат информацию о названии и размере шрифта, цвете символа, его стиле и некоторую другую. Поэтому длина файла, измеренная в байтах, может значительно превышать число содержащихся в них символов. В качестве расширения таких файлов обычно используется указание на текстовый редактор, при помощи которого был создан файл. Например, .doc, .docx, .pdf.

В настоящее время для создания текстовых файлов можно использовать специализированные устройства ввода, называемые оптическими сканерами. Исходной информацией для сканера служит текст на бумажном носителе. Сканер создает в памяти изображение страницы с текстом, а программ распознавания символов Optical Character Recognition (OCR) преобразует изображение каждого символа в ASCII-код и записывает в текстовый файл без участия человека. Сканеры широко применяются при переносе информации с бумажных носителей на машинные.

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

Приложения (или программы) также хранятся в файлах. Файл, содержащий текст приложения на языке программирования и поэтому требующий компиляции, называют, обычно, исходным. Расширение таких файлов зависит от использованного языка программирования. Например, расширение .cpp используется для файлов с исходным текстом на языке Си, а расширение .cs – для файлов на C#. Файлы с откомпилированными приложениями принято называть объектными. Они имеют расширение .obj. А файлы, готовые к исполнению называются исполняемыми. Они имеют расширение .exe или .com.

Оказывается, что одну и ту же информацию можно хранить в файле различными способами. Например, вещественное чис­ло 123.4532 можно хранить как последовательность из восьми отдельных символов алфавита или как объект типа float. В первом случае для его хранения потребуется восемь байт, а во втором - только четыре. Способ хранения (представления) информации в файле называется его форматом или структурой. Получение доступа к содержимому файла без знания его структуры может оказаться достаточно сложной задачей. Как показывает практика, производители программного обеспечения часто используют для представления информации собственные форматы. Это обстоятельство серьезно осложняет обмен информацией между ее потребителями. Преобразование файла из одного формата в другой называется конвертированием, а программа, выполняющая такое преобразование, - конвертором. К настоящему времени написаны конверторы для большинства широко используемых форматов текстовых и графических файлов.

6.3 Буферизация обмена. Открытие и закрытие файлов

Буферизация ввода и вывода. Структура типа FILE. Открытие и закрытие файлов. Бинарные и текстовые файлы. Текущая позиция.

Основными операциями при работе с файлами являются запись информации в файл (вывод) и чтение информации из файла (ввод). Для их выполнения в стандартной библиотеке Си содержится достаточно большое количество функций.

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

Кроме буфера для организации обмена с диском требуется дополнительная (вспомогательная) информация. Она хранится в структуре типа FILE, который определен в файле с именем stdio.h, и содержит шесть переменных. Обмен с файлом начинается с создания буфера и структуры типа FILE. Эта операция называется открытием файла и выполняется посредством вызова стандартной функции fopen() с прототипом

FILE* fopen(

// Строка с полным именем файла

const char* szFileName,

// Строка с типом операции

const char* szModeAccess);

который хранится в файле stdio.h. Первым параметром функции fopen() является строка с полным именем файла, а вторым - строка с типом операции (режимом доступа к файлу). Назначения возможных режимов описаны в табл. 7.1.

Таблица 7.1 - Режимы доступа файлу

Обозначение

Режима

Описание режима

r

Чтение из существующего файла. Если файл с указанным именем не существует, то функция возвращает 0.

w

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

a

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

r+

Чтение и запись в существующий файл. Если файл с указанным именем не существует, то функция возвращает 0.

w+

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

a+

Чтение и добавление. Если файл с указанным именем не обнаружен, то он создается.

Если в строку с режимом доступа (второй параметр функции fopen()) добавляется буква t (от слова text), то в этом случае все пары, состоящие из управляющих символов ‘\r‘ - возврат каретки и ‘\n‘ - новая строка, при чтении из файла заменяются на символ ‘\n‘. А при записи в файл каждый управляющий символ ‘\n‘ заменяется на пару ‘\r‘ и ‘\n‘. Для того, чтобы запретить эти преобразования, в строку с режимом доступа необходимо добавить букву b (от слова binary). При отсутствии явных указаний со стороны программиста режим доступа задается с помощью глобальной переменной _fmode.

При успешном завершении функция fopen() возвращает адрес структуры. Если создать структуру не удалось, она возвращает константу NULL типа FILE*.

Завершение работы с файлом должно включать запись в него содержимого буфера и освобождение памяти, занимаемой структурой, указатель на которую был получен при его открытии. Совокупность этих операций называется закрытием файла и выполняется посредством вызова стандартной функции с именем fclose() и прототипом

int fclose(

// адрес структуры типа FILE

FILE* pfile);

При успешном завершении она возвращает число нуль типа int, а в противном случае константу EOF=-1, определенную в файле <stdio.h>.

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

void exit(

// Код завершения

int n);

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

Пример 6.1. Открытие и закрытие файлов

// Подключение файлов с прототипами функций

#include <stdio.h>

void main(void) {

char AccessMode[6];

// Ввод режима доступа к файлу

printf("Access mode: ");

gets(AccessMode);

// Открытие текстового файла

FILE* pFile=fopen("MyFile.txt", AccessMode);

// Проверка корректности открытия файла

if(!pFile) {

// Действия при неудачном открытии

printf("Error! The file is not opened\n");

return ;

}

// Действия при корректном открытии файла

printf("The file is opened\n");

// Закрытие файла

fclose(pFile);

}

Обычно файл рассматривают как упорядоченную последовательность байтов, называемую потоком (stream). В структуре типа FILE, создаваемой при открытии файла, имеется переменная типа long int, которая называется текущей позицией или внутренним указателем файла. При открытии файла для чтения или записи ее значение равняется нулю, а при открытии файла для дополнения – его длине. Очередная запись или чтение начинается с байта, номер которого совпадает с текущей позицией. После очередной записи или чтения значение текущей позиции увеличивается на количество записанных (прочитанных) байтов. Узнать значение текущей позиции можно посредством вызова стандартной функции с именем ftell() и прототипом

long ftell(

// Адрес структуры типа FILE

FILE* pFile);

Ее единственным параметром является адрес структуры типа FILE, полученный при открытии файла. При успешном завершении функция возвращает число типа long int - значение текущей позиции. В остальных случаях число -1L.

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

int fseek(

// Адрес структуры типа FILE

FILE* pFile,

// Величина требуемого смещения

long lOffset,

// Начало отсчета

int iOrigin);

Ее первым параметром является адрес структуры типа FILE, полученный при открытии файла. Второй параметр задает величину смещения текущей позиции относительно начала отсчета iOrigin, задаваемого третьим параметром. Если iOrigin=0, то за начало отсчета принимают число равное нулю. В случае iOrigin=1 началом отсчета является текущая позиция, а при iOrigin=2 за начало отсчета принимают число равное длине файла. Вместо констант 0, 1 и 2 типа int разрешается использовать их имена SEEK_SET, SEEK_CUR и SEEK_END соответственно.

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

Для установки текущей позиции на начало файла можно использовать функцию с прототипом

void rewind(FILE* pFile);

Функция fseek() часто используется для определения длины файла.

Пример 6.2. Вычисление длины файла

#include<stdio.h>

void main(){

// Открытие текстового файла

FILE* pFile=fopen("file_1.cpp", "r");

// Проверка корректности открытия файла

if(!pFile) {

// Действия при неудачном открытии

printf("Error! The file is not opened\n");

return ;

}

// Действия при корректном открытии файла

printf("The file is opened\n");

long pos=ftell(pFile);

printf("pos=%d \n",pos);

// Перемещение текущей позиции в конец файла

fseek(pFile,0,SEEK_END);

printf("pos=%d \n",ftell(pFile));

// Закрытие файла

fclose(pFile);

printf("The file is closed \n");

}

6.4. Запись и чтение объектов основных типов

Стандартные функции с именами fprintf() и fscanf() позволяют записывать в файл и читать из него объект любого из основных типов. Их прототипы отличаются от прототипов соответствующих функций printf() и scanf() наличием дополнительного параметра, который является адресом структуры типа FILE:

int fprintf(

// Адрес структуры типа FILE

FILE* pFile,

// Адрес строки с управляющей информацией

const char* szString,

// И возможно другие параметры

...);

int fscanf(

// Адрес структуры типа FILE

FILE* pFile,

// Адрес строки с управляющей информацией

const char* szString,

// И возможно другие параметры

...);

При этом функция fprintf() возвращает количество записанных в файл байтов, а функция fscanf() - количество прочитанных из файла объектов. При неудачной попытке чтения (когда, например, текущая позиция равна длине файла) fscanf() возвращает константу EOF.

Следует иметь в виду, что для правильного чтения из файла с помощью функции fscanf() необходимо, чтобы находящиеся в файле объекты были разделены между собой любым из следующих трех символов, называемых разделителями: ‘\n‘, ‘\t‘ или пробелом. Именно по этой причине в нижеследующем примере каждая спецификация в функции fprintf() заканчивается символом новая строка ‘\n‘. Функция fscanf() читает последовательность символов, начиная с текущей позиции до первого разделителя, и преобразует ее (последовательность) в объект указанного типа. Если последовательность начинается с разделителей, то они пропускаются. Так как разделители являются объектами типа char, то при чтении объекта типа char приходится предварительно прочитать предшествующий ему разделитель. Этой причиной объясняется появление дополнительного вызова функции fscanf() со спецификацией %c при чтении объекта типа char в нижеследующем примере. После записи каждого объекта в файл на экране дисплея отображается его текущая длина.

Пример 6.3 - Запись и чтение объектов основных типов

#include <stdio.h>

void main() {

// Создание указателя на тип FILE

FILE* pFile;

// Открытие файла для чтения и записи

pFile=fopen("Exemple.txt", "w+b");

// Проверка корректности результата

if(pFile==NULL) {

// Действия при неудачном открытии файла

printf("Error! The file is not opened\n");

return ;

}

// Запись строки: 14+1=15 символов

fprintf(pFile, "%s\n", "Write_and_read");

// Запись объекта типа int: 2+1=3 символа

fprintf(pFile, "%d\n", 12);

// Запись объекта типа int: 10+1=11 символов

fprintf(pFile, "%10d\n", 12);

// Запись объекта типа float: 1+1+6+1=9 символов

fprintf(pFile, "%f\n", 4.5);

// Запись объекта типа float: 1+1+10+1=13 символов

fprintf(pFile, "%.10f\n", 4.5);

// Запись объекта типа char: 1+1=2 символа

fprintf(pFile, "%c\n", 'A');

//Отображение длины созданного файла

printf("LenFile: %ld\n", ftell (pFile)); // 53

// Чтение из файла

// Установка внутреннего указателя на начало файла

fseek(pFile, 0L, SEEK_SET);

printf("PosCur: %ld\n", ftell (pFile)); // 0

// Выделение памяти для читаемых из файла данных

char szString[81];

int i;

float f;

char ch,temp;

// Чтение

fscanf(pFile, "%s", szString);

printf("PosCur: %ld\n", ftell (pFile)); // 14

printf("%s\n", szString);

fscanf(pFile, "%d", &i);

printf("PosCur: %ld\n", ftell (pFile)); // 17

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

fscanf(pFile, "%d", &i);

printf("PosCur: %ld\n", ftell (pFile)); // 28

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

fscanf(pFile, "%f", &f);

printf("PosCur: %ld\n", ftell (pFile)); // 37

printf("%f\n", f);

fscanf(pFile, "%f", &f);

printf("PosCur: %ld\n", ftell (pFile)); // 50

printf("%f\n", f);

fscanf(pFile, "%c", &temp);

printf("PosCur: %ld\n", ftell (pFile)); // 51

printf("%c\n", temp);

fscanf(pFile, "%c", &ch);

printf("PosCur: %ld\n", ftell (pFile)); // 52

printf("%c\n", ch);

// Попытка чтения

int Count=11,NonEx=22;

Count=fscanf(pFile, "%d", &NonEx);

printf("Count=%d\n", Count); // -1

printf("NonEx=%d\n", NonEx); // 22

printf("PosCur: %ld\n", ftell (pFile)); // 53

// Закрытие файла

fclose(pFile);

}

После выполнения приведенной программы на экране дисплея должно появиться следующее сообщение:

LenFile: 53

PosCur: 0

PosCur: 14

Write_and_read

PosCur: 17

12

PosCur: 28

12

PosCur: 37

4.500000

PosCur: 50

4.500000

PosCur: 51

PosCur: 52

A

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

В заключении рассмотрим еще одно приложение. Оно позволяет записывать в файл матрицу. При этом в файл кроме самих элементов матрицы приходится записывать и ее размеры. В этом же приложении осуществляется чтение из файла элемента матрицы из i-ой строки и j-ого столбца. Эта операция выполняется с помощью функции ScanfFromFile().

Пример 6.4. Запись матрицы и чтение ее элемента

#include <stdio.h>

double ScanfFromFile(char* FileName, int i, int j);

void main(void){

// Размеры матрицы

int h=3, w=4;

// Матрица

double A[]={0.0, 0.1, 0.2, 0.3,

1.0, 1.1, 1.2, 1.3,

2.0, 2.1, 2.2, 2.3};

// Имя файла

char FileName[]="Matrix.dat";

// Открытие файла для записи

FILE* pFile=fopen(FileName, "w+b");

// Проверка

if(!pFile){

printf("The file for a record was not opened!\n");

return;

}

// Запись размеров матрицы

fprintf(pFile, "%d\n",h);

fprintf(pFile, "%d\n",w);

// Запись элементов матрицы

int Dim=h*w;

for(int i=0; i<Dim; i++){

fprintf(pFile, "%f\n", A[i]);

}

printf("File lenght: %d\n", ftell(pFile));

fclose(pFile);

// Чтение элемента матрицы из i-ой строки и j-ого столбца

char Buf[4];

int i, j;

while(1){

printf("Exit? (yes or no): ");

scanf("%s", Buf);

if(Buf[0]=='y')

break;

printf("Input Index Line: ");

scanf("%d", &i);

if((i<0)||(i>=h)){

printf("Bad Index!\n");

continue;

}

printf("Input Index Colonm: ");

scanf("%d", &j);

if((j<0)||(j>=w)){

printf("Bad Index!\n");

continue;

}

double Res=ScanfFromFile(FileName, i, j);

printf("A[%d][%d]=%f\n", i, j, Res);

}

}

double ScanfFromFile(char* FileName, int i, int j){

// Открытие

FILE* pFile=fopen(FileName, "r+b");

// Проверка

if(!pFile){

printf("Error!\n");

return 0;

}

// Чтение размеров

int h,w;

fscanf(pFile, "%d\n", &h);

fscanf(pFile, "%d\n", &w);

// Вычисление порядкового номера элемента

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]