Каждый класс файлового потока содержит файловый указатель, который используется для отслеживания текущей позиции чтения/записи в файле. Когда что-то читается или записывается в файл, чтение/запись происходит в текущем местоположении этого файлового указателя. По умолчанию при открытии файла для чтения или записи файловый указатель устанавливается на начало файла. Однако если файл открывается в режиме добавления, файловый указатель перемещается в конец файла, и поэтому какое-либо текущее содержимое файла не перезаписывается.
Произвольный доступ к файлам с помощью seekg() и seekp()
До сих пор весь доступ к файлам, который мы выполняли, был последовательным, то есть мы читали или записывали содержимое файла по порядку. Однако также к файлу можно выполнять произвольный доступ, то есть переходить к различным точкам в файле, чтобы прочитать его содержимое. Это может быть полезно, когда ваш файл содержит много записей, и вы хотите получить определенную из них. Вместо того чтобы читать все записи, пока не дойдете до нужной, вы можете сразу перейти к записи, которую хотите получить.
Работа с файлами с++. Чтение из файла с++ ifstream. Изучение С++ для начинающих. Урок #116
Произвольный доступ к файлу осуществляется путем манипулирования файловым указателем с помощью функции seekg() (для ввода) и функции seekp() (для вывода). Если вам интересно, g означает «get» (получить), а p – «put» (положить). Для некоторых типов потоков seekg() (изменение позиции чтения) и seekp() (изменение позиции записи) работают независимо, однако с файловыми потоками позиции чтения и записи всегда идентичны, поэтому seekg и seekp могут использоваться взаимозаменяемо.
Функции seekg() и seekp() принимают два параметра. Первый параметр – это смещение, определяющее, на сколько байтов переместить файловый указатель. Второй параметр – это флаг ios , который указывает, от чего должен быть смещен параметр смещения.
beg | Смещение относительно начала файла (по умолчанию) |
cur | Смещение относительно текущего местоположения файлового указателя |
end | Смещение относительно конца файла |
Положительное смещение означает перемещение файлового указателя к концу файла, тогда как отрицательное смещение означает перемещение файлового указателя к началу файла.
Вот несколько примеров:
inf.seekg(14, ios::cur); // перейти вперед на 14 байтов inf.seekg(-18, ios::cur); // перейти назад на 18 байтов inf.seekg(22, ios::beg); // перейти к 22-му байту в файле inf.seekg(24); // перейти к 24-му байту в файле inf.seekg(-28, ios::end); // перейти к 28-му байту с конца файла
Перейти к началу или концу файла очень просто:
inf.seekg(0, ios::beg); // перейти к началу файла inf.seekg(0, ios::end); // перейти в конец файла
Давайте рассмотрим пример, используя seekg() и входной файл, который мы создали в прошлом уроке. Этот входной файл выглядит так:
This is line 1 This is line 2 This is line 3 This is line 4
А вот сам пример:
Гайд по блокноту | Трюки и фишки в блокноте
int main() < std::ifstream inf< «Sample.dat» >; // Если мы не смогли открыть входной файловый поток для чтения if (!inf) < // Распечатываем ошибку и выходим std::cerr std::string strData; inf.seekg(5); // переход к 5-му символу // Получаем оставшуюся часть строки и распечатываем ее getline(inf, strData); std::cout
Этот код дает следующий результат:
is line 1 line 2 his is line 4
Примечание. Некоторые компиляторы содержат ошибки в реализации seekg() и seekp() при использовании вместе с текстовыми файлами (из-за буферизации). Если ваш компилятор является одним из них (о чем вы узнаете, если ваш вывод будет отличаться от приведенного выше), вы можете вместо этого попробовать открыть файл в двоичном режиме:
ifstream inf(«Sample.dat», ifstream::binary);
Еще две полезные функции – это tellg() и tellp() , которые возвращают абсолютную позицию файлового указателя. Их можно использовать для определения размера файла:
std::ifstream inf(«Sample.dat»); inf.seekg(0, std::ios::end); // перейти в конец файла std::cout
Этот код печатает:
Это длина файла sample.dat в байтах (при условии возврата каретки после последней строки).
Одновременные чтение и запись файла с помощью fstream
Класс fstream может читать и записывать файл одновременно (почти)! Самое большое неудобство здесь в том, что невозможно переключаться между чтением и записью произвольно. После чтения или записи единственный способ переключиться между ними – выполнить операцию, изменяющую позицию файла. Если вы на самом деле не хотите перемещать файловый указатель (потому что он уже находится в нужном вам месте), вы всегда можете перейти к текущей позиции:
// предполагаем, что iofile — это объект типа fstream iofile.seekg(iofile.tellg(), ios::beg); // переходим к текущей позиции в файле
Если вы этого не сделаете, может произойти множество странных и причудливых вещей.
Примечание: хотя может показаться, что iofile.seekg(0, ios::cur) также будет работать, похоже, что некоторые компиляторы могут оптимизировать этот вызов.
Еще одна хитрость: в отличие от ifstream , где мы могли бы сказать while(inf) , чтобы определить, есть ли что-то еще для чтения, с fstream это работать не будет.
Давайте напишем пример файлового ввода/вывода с помощью fstream . Мы собираемся написать программу, которая открывает файл, читает его содержимое и заменяет любые найденные гласные на символ ‘ # ‘.
int main() < // Обратите внимание, что мы должны указать как in, // так и out, потому что мы используем fstream std::fstream iofile< «Sample.dat», ios::in | ios::out >; // Если не удалось открыть iofile, выводим ошибку if (!iofile) < // Распечатываем ошибку и выходим std::cerr char chChar<>; // мы собираемся выполнить задачу посимвольно // Пока есть данные для обработки while (iofile.get(chChar)) < switch (chChar) < // Если найдем гласную case ‘a’: case ‘e’: case ‘i’: case ‘o’: case ‘u’: case ‘A’: case ‘E’: case ‘I’: case ‘O’: case ‘U’: // Возвращаемся на один символ iofile.seekg(-1, std::ios::cur); // Поскольку мы выполнили переход, // теперь мы можем безопасно выполнить запись, // поэтому давайте перезапишем гласную символом # iofile > return 0; >
Другие полезные функции работы с файлами
Чтобы удалить файл, просто используйте функцию remove() .
Функция is_open() вернет true , если поток в данный момент открыт, и false в противном случае.
Предупреждение о записи указателей на диск
Хотя потоковая передача переменных в файл довольно проста, всё становится сложнее, когда вы имеете дело с указателями. Помните, что указатель просто содержит адрес переменной, на которую он указывает. Хотя адреса можно читать и записывать на диск, это чрезвычайно опасно. Это связано с тем, что адрес переменной при каждом выполнении может различаться. Следовательно, хотя переменная могла существовать по адресу 0x0012FF7C , когда вы записывали этот адрес на диск, она может больше не находиться там, когда вы читаете этот адрес обратно!
Например, предположим, что у вас есть целое число с именем nValue , которое находится по адресу 0x0012FF7C . Вы присвоили nValue значение 5. Вы также объявили указатель с именем *pnValue , который указывает на nValue . pnValue содержит адрес nValue – 0x0012FF7C . Вы хотите сохранить их на потом, поэтому вы записываете на диск значение 5 и адрес 0x0012FF7C .
Через несколько недель вы снова запускаете программу и считываете эти значения с диска. Вы считываете значение 5 в другую переменную с именем nValue , которая находится по адресу 0x0012FF78 . Вы считываете адрес 0x0012FF7C в новый указатель с именем *pnValue . Поскольку pnValue теперь указывает на 0x0012FF7C , а nValue находится на 0x0012FF78 , pnValue больше не указывает на nValue , и попытка доступа к pnValue приведет к проблемам.
Правило: Не записывайте адреса в файлы. Переменные, которые изначально находились по этим адресам, могут находиться уже по другим адресам, когда вы будете считывать их значения обратно с диска, и эти адреса будут недействительными.
Источник: radioprog.ru
Запись и считывание данных ( работа с файлами )
В этом разделе будут рассотрены два способа работы с фыйлами и стандартный класс MFC CFileDialog.
Работа с файлами в C ( работает и в C++ )..
#include #include void main( void ) < FILE *file; char* file_name = «file.txt»; char load_string[50] = «none»; file = fopen( file_name, «w» ); fputs( «string», file ); fclose( file ); file = fopen( file_name, «r» ); if( file != 0 ) < fgets( load_string, 50 , file ); cout
Описание функций работы с файломи находятся в библиотеке stdio.h
Сначала надо создать указатель на переменную типа FILE ( FILE* file; ). Открытие файла производится вызовом функции fopen ( file = fopen( file_name, «w» ); ) Первый параметр этой функции — имя файла, второй — указывает в каком режиме должен быть открыт файл. «w» — открыть для записи, «r» — открыть для чтения, «a» — дополнение файла( это наиболее используемые режимы, хотя есть и другие ). Запись и считывание данных из файла осуществляется следующими функциями : fputc, fputs, fgetc, fgets, fprintf, fscanf( описание этих функций смотрите в stdio.h). Закрытие файла осуществляется вызовом функции fclose ( fclose( file ); ).
Работа с файлами с помощью MFC( классы CFile, CStdioFile, . ) и стандартный класс MFC CFileDialog.
В библиотеку MFC включено несколько классов для обеспечения работы с файлами. Рассматриваемые ниже классы наследуются от базового класса CFile.
Класс CFile
Класс CFile предназначен для обеспечения работы с файлами. Он позволяет упростить использование файлов, представляя файл как объект, который можно создать, читать, записывать и т.д.
Чтобы получить доступ к файлу, сначала надо создать объект класса CFile. Конструктор класса позволяет сразу после создания такого объекта открыть файл. Но можно открыть файл и позднее, воспользовавшись методом Open.
Открытие и создание файлов
После создания объекта класса CFile можно открыть файл, вызвав метод Open. Методу надо указать путь к открываемому файлу и режим его использования. Прототип метода Open имеет следующий вид:
virtual BOOL Open(LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pError=NULL);
В качестве параметра lpszFileName надо указать имя открываемого файла. Можно указать только имя файла или полное имя файла, включающее полный путь к нему.
Второй параметр nOpenFlags определяет действие, выполняемое методом Open с файлом, а также атрибуты файла. Ниже представлены некоторые возможеые значения параметра nOpenFlags:
- CFile::modeCreate — Создается новый файл. Если указанный файл существует, то его содержимое стирается и длина файла устанавливается равной нулю.
- CFile::modeNoTruncate — Этот файл предназначен для использования совместно с файлом CFile::modeCreate. Если создается уже существующий файл, то его содержимое не будет удалено.
- CFile::modeRead — Файл открывается только для чтения.
- CFile::modeReadWrite — Файл открывается для записи и для чтения.
- CFile::modeWrite — Файл открывается только для записи.
- CFile::typeText — Используется классами, порожденными от класса CFile, например CStdioFile, для работы с файлами в текстовом режиме. Текстовый режим обеспечивает преобразование комбинации символа возврата каретки и символа перевода строки.
- CFile::Binary — Используется классами, порожденными от класса CFile, например CStdioFile, для работы с файлами в двоичном режиме.
Необязательный параметр pError, который является указателем на объект класса CFileException, используется только в том случае, если выполнение операции с файлом вызовет ошибку. При этом в объект, указываемый pError, будет записана дополнительная информация.
Метод Open возвращает не нулевое значение, если файл открыт и нуль в случае ошибки. Ошибка при открытии файла может случиться, например, если методу Open указан для чтения несуществующий файл.
Идентификатор открытого файла
В состав класса CFile входит элемент данных m_hFile типа UINT. В нем хранится идентификатор открытого файла. Если объект класса CFile уже создан, но файл еще не открыт, то в переменной m_hFile записана константа hFileNull.
Обычно идентификатор открытого файла непосредственно не используется. Методы класса CFile позволяют выполнять практически любые операции с файлами и не требуют указывать идентификатор файла. Так как m_hFile является элементом класса, то реализация его методов всегда имеет свободный доступ к нему.
Закрытие файлов
После завершения работы с файлом, его надо закрыть. Класс CFile имеет для этого специальный метод Close. Нужно заметить, что если был создан объект класса CFile и открыт файл, а затем объект удаляется, то связанный с ним файл закрывается автоматически с помощью деструктора.
Чтение и запись файлов
Для доступа к файлам предназначено несколько методов класса CFile: Read, ReadHuge, Write, WriteHuge, Flush. Методы Read и ReadHuge предназначены для чтения данных из предварительно открытого файла. В 32-разрядных операционных системах оба метода могут одновременно считать из файла больше 65535 байт. Спецификация ReadHuge считается устаревшей и оставлена только для совместимости с 16-разрядными операционными системами.
Данные, прочитанные из файла, записываются в буфер lpBuf. Параметр nCount определяет количество байт, которое надо считать из файла. Фактически из файла может быть считано меньше байт, чем запрошено параметром nCount. Это происходит, если во время чтения достигнут конец файла. Методы возвращают количество байт, прочитанных из файла.
Для записи в файл предназначены методы Write и WriteHuge. В 32-разрядных операционных системах оба метода могут одновременно записывать в файл больше 65535 байт. Методы записывает в открытый файл nCount байт из буфера lpBuf. В случае возникновения ошибки записи, например переполнения диска, методы вызывает обработку исключения.
Метод Flush
Когда используется метод Write или WriteHuge для записи данных на диск, они некоторое время могут находиться во временном буфере. Чтобы убедиться, что необходимые изменения внесены в файл на диске, нужно воспользоваться методом Flush.
Операции с файлами
В состав класса входят методы, позволяющие выполнять над файлами различные операции, например копирование, переименование, удаление, изменение атрибутов.
Для изменения имени файла класс CFile включает статический метод Rename, выполняющий функции этой команды. Метод нельзя использовать для переименования каталогов. В случае возникновения ошибки метод вызывает исключение.
Для удаления файлов в классе CFile включен статический метод Remove, позволяющий удалить указанный файл. Этот метод не позволяет удалять каталоги. Если удалить файл невозможно, то метод вызывает исключение.
Чтобы определить дату и время создания файла, его длину и атрибуты, предназначен статический метод GetStatus. Существует две разновидности метода — первый определен как виртуальный, а второй — как статический метод.
Виртуальная версия метода GetStatus определяет состояние открытого файла, связанного с данным объектом класса CFile. Этот метод вызывается только тогда, когда объект класса CFile создан и файл открыт.
Статическая версия метода GetStatus позволяет определить характеристики файла, не связанного с объектом класса CFile. Чтобы воспользоваться этим методом, необязательно предварительно открывать файл.
Блокировка
В состав класса включены методы LockRange и UnlockRange, позволяющие заблокировать один или несколько фрагментов данных файла для доступа из других процессов. Если приложение пытается повторно блокировать данные, уже заблокированные раньше этим или другим приложением, вызывается исключение. Блокировка представляет собой один из механизмов, позволяющих нескольким приложениям или процессам одновременно работать с одним файлом, не мешая друг другу.
Установить блокировку можно с помощью метода LockRange. Чтобы снять установленные блокировки, надо воспользоваться методом UnlockRange. Если в одном файле установлены несколько блокировок, то каждая из них должна сниматься отдельным вызовом метода UnlockRange.
Позиционирование
Чтобы переместить указатель текущей позиции файла в новое положение, можно воспользоваться одним из следующих методов класса CFile — Seek, SeekToBegin, SeekToEnd. В состав класса CFile также входят методы, позволяющие установить и изменить длину файла, — GetLength, SetLength.
При открытии файла указатель текущей позиции файла находится в самом начале файла. Когда порция данных прочитана или записана, то указатель текущей позиции перемещается в сторону конца файла и указывает на данные, которые будут читаться или записываться очередной операцией чтения или записи в файл.
Чтобы переместить указатель текущей позиции файла в любое место, можно воспользоваться универсальным методом Seek. Он позволяет переместить указатель на определенное число байт относительно начала, конца или текущей позиции указателя.
Чтобы переместить указатель в начало или конец файла, наиболее удобно использовать специальные методы. Метод SeekToBegin перемещает указатель в начало файла, а метод SeekToEnd — в его конец.
Но для определения длины открытого файла совсем необязательно перемещать его указатель. Можно воспользоваться методом GetLength. Этот метод также возвращает длину открытого файла в байтах. Метод SetLength позволяет изменить длину открытого файла. Если при помощи этого метода размер файла увеличивается, то значение последних байт не определено.
Текущую позицию указателя файла можно определить с помощью метода GetPosition. Возвращаемое методом GetPosition 32-разрядное значение определяет смещение указателя от начала файла.
Характеристики открытого файла
Чтобы определить расположение открытого файла на диске, надо вызвать метод GetFilePath. Этот метод возвращает объект класса CString, в котором содержится полный путь файла, включая имя диска, каталоги, имя файла и его расширение.
Если требуется определить только имя и расширение открытого файла, можно воспользоваться методом GetFileName. Он возвращает объект класса CString, в котором находится имя файла. В случае, когда нужно узнать только имя открытого файла без расширения, пользуются методом GetFileTitle.
Следующий метод класса CFile позволяет установить путь файла. Это метод не создает, не копирует и не изменяет имени файла, он только заполняет соответствующий элемент данных в объекте класса CFile.
Класс CMemFile
В библиотеку MFC входит класс CMemFile, наследуемый от базового класса CFile. Класс CMemFile представляет файл, размещенный, в оперативной памяти. С объектами класса CMemFile так же, как и с объектами класса CFile. Отличие заключается в том, что файл, связанный с объектом CMemFile, расположен не на диске, а в оперативной памяти компьютера. За счет этого операции с таким файлом происходят значительно быстрее, чем с обычными файлами.
Работая с объектами класса CMemFile, можно использовать практически все методы класса CFile, которые были описаны выше. Можно записывать данные в такой файл или считывать их. Кроме этих методов в состав класса CMemFile включены дополнительные методы.
Для создания объектов класса CMemFile предназначено два различных конструктора. Первый конструктор CMemFile имеет всего один необязательный параметр nGrowBytes:
CMemFile(UINT nGrowBytes=1024);
Этот конструктор создает в оперативной памяти пустой файл. После создания файл автоматически открывается (не нужно вызывать метод Open).
Когда начинается запись в такой файл, автоматически выделяется блок памяти. Для получения памяти методы класса CMemFile вызывают стандартные функции malloc, realloc и free. Если выделенного блока памяти недостаточно, его размер увеличивается. Увеличение блока памяти файла происходит по частям по nGrowBytes байт. После удаления объекта класса CMemFile используемая память автоматически возвращается системе.
Второй конструктор класса CMemFile имеет более сложный прототип. Это конструктор используется в тех случаях, когда программист сам выделяет память для файла:
CMemFile(BYTE* lpBuffer, UINT nBufferSize, UINT nGrowBytes=0);
Параметр lpBuffer указывает на буфер, который будет использоваться для файла. Размер буфера определяется параметром nBufferSize.
Необязательный параметр nGrowBytes используется более комплексно, чем в первом конструкторе класса. Если nGrowBytes содержит нуль, то созданный файл будет содержать данные из буфера lpBuffer. Длина такого файла будет равна nBufferSize.
Если nGrowBytes больше нуля, то содержимое буфера lpBuffer игнорируется. Кроме того, если в такой файл записывается больше данных, чем помещается в отведенном буфере, то его размер автоматически увеличивается. Увеличение блока памяти файла происходит по частям по nGrowBytes байт.
Класс CMemFile позволяет получить указатель на область памяти, используемую файлом. Через этот указатель можно непосредственно работать с содержимым файла, не ограничивая себя методами класса CFile. Для получения указателя на буфер файла можно воспользоваться методом Detach. Перед этим полезно определить длину файла (и соответственно размер буфера памяти), вызвав метод GetLength.
Метод Detach закрывает данный файл и возвращает указатель на используемый им блок памяти. Если опять потребуется открыть файл и связать с ним оперативный блок памяти, нужно вызвать метод Attach.
Нужно отметить, что для управления буфером файла класс CMemFile вызывает стандартные функции malloc, realloc и free. Поэтому, чтобы не нарушать механизм управления памятью, буфер lpBuffer должен быть создан функциями malloc или calloc.
Класс CStdioFile
Тем, кто привык пользоваться функциями потокового ввода/вывода из стандартной библиотеки C и C++, следует обратить внимание на класс CStdioFile, наследованный от базового класса CFile. Этот класс позволяет выполнять буферизированный ввод/вывод в текстовом и двоичном режиме. Для объектов класса CStdioFile можно вызывать практически все методы класса CFile.
В класс CStdioFile входит элемент данных m_pStream, который содержит указатель на открытый файл. Если объект класса CStdioFile создан, но файл еще не открыт, либо закрыт, то m_pStream содержит константу NULL.
Класс CStdioFile имеет три различных конструктора. Первый конструктор класса CStdioFile не имеет параметров. Этот конструктор только создает объект класса, но не открывает никаких файлов. Чтобы открыть файл, надо вызвать метод Open базового класса CFile.
Второй конструктор класса CStdioFile можно вызвать, если файл уже открыт и нужно создать новый объект класса CStdioFile и связать с ним открытый файл. Этот конструктор можно использовать, если файл был открыт стандартной функцией fopen. Параметр метода должен содержать указатель на файл, полученный вызовом стандартной функции fopen.
Третий конструктор можно использовать, если надо создать объект класса CStdioFile, открыть новый файл и связать его с только что созданным объектом.
Для чтения и записи в текстовый файл класс CStdioFile включает два новых метода: ReadString и WriteString. Первый метод позволяет прочитать из файла строку символов, а второй метод — записать.
Примеры записи и чтения из файла
Приведем фрагменты кода, в которых демонстрируется использование стандартных диалоговых панелей выбора файла и процедуры чтения и записи в файл.
Открытие файла и чтение из него
CString m_Text; // создание стандартной панели выбора файла Open CFileDialog DlgOpen(TRUE,(LPCSTR)»txt»,NULL, OFN_HIDEREADONLY,(LPCSTR)» Text Files (*.txt) |*.txt||»); // отображение стандартной панели выбора файла Open if(DlgOpen.DoModal()==IDOK) < // создание объекта и открытие файла для чтения CStdioFile File(DlgOpen.GetPathName(),CFile:: modeRead|CFile::typeBinary); // чтение из файла строки CString File.ReadString(ref); // передается ссылка на строку m_Text >
Запустите программу — Build / Rebuild all ( будут ошибки ), выберите Build / Set active configuration — Win 32 Realise, выберите пункт меню «Project», далее «Settings. «, закладку «C/C++», Category — Code Generation и в пункте «Use run-time library» выберите «Multithreaded». После этого сделайте опять Build / Rebuild all и программа будет работать.
Открытие файла и запись из него
CString m_Text; // создание стандартной панели выбора файла SaveAs CFileDialog DlgSaveAs(FALSE,(LPCSTR)»txt»,NULL, OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT, (LPCSTR)» Text Files (*.txt) |*.txt||»); // отображение стандартной панели выбора файла SaveAs if(DlgSaveAs.DoModal()==IDOK) < // создание объекта и открытие файла для записи CStdioFile File(DlgSaveAs.GetPathName(), CFile::modeCreate|CFile::modeWrite|CFile::typeBinary); // запись в файл строки File.WriteString((LPCTSTR)m_Text); >
Запустите программу — Build / Rebuild all ( будут ошибки ), выберите Build / Set active configuration — Win 32 Realise, выберите пункт меню «Project», далее «Settings. «, закладку «C/C++», Category — Code Generation и в пункте «Use run-time library» выберите «Multithreaded». После этого сделайте опять Build / Rebuild all и программа будет работать.
Источник: codenet.ru
Учебники. Программирование для начинающих.
оценка ос НПО Актив является организацией, выполняющей как независимую оценку, так и независимую экспертизу стоимости. Наши эксперты определят соответствие отчета требованиям Федерального Закона № 135 “Об оценочной деятельности”, а также соответствие Федеральным законам оценки. В случае, если есть необходимость в судебном порядке подтвердить или опровергнуть стоимость имущества, наши судебные эксперты готовы оказать услугу по судебной экспертизе стоимости бизнеса.
Programm.ws — это сайт, на котором вы можете почитать литературу по языкам программирования , а так-же посмотреть примеры работающих программ на С++, ассемблере, паскале и много другого..
Программирование — в обычном понимании, это процесс создания компьютерных программ.
В узком смысле (так называемое кодирование) под программированием понимается написание инструкций — программ — на конкретном языке программирования (часто по уже имеющемуся алгоритму — плану, методу решения поставленной задачи). Соответственно, люди, которые этим занимаются, называются программистами (на профессиональном жаргоне — кодерами), а те, кто разрабатывает алгоритмы — алгоритмистами, специалистами предметной области, математиками.
В более широком смысле под программированием понимают весь спектр деятельности, связанный с созданием и поддержанием в рабочем состоянии программ — программного обеспечения ЭВМ. Более точен современный термин — «программная инженерия» (также иначе «инженерия ПО»). Сюда входят анализ и постановка задачи, проектирование программы, построение алгоритмов, разработка структур данных, написание текстов программ, отладка и тестирование программы (испытания программы), документирование, настройка (конфигурирование), доработка и сопровождение.
Ассемблер — примеры и задачи
Глава 7. Работа с файлами в программах на ассемблере
Чтение, запись, позиционирование в файле
При работе с материалом данного раздела помните, что функции чтения и записи можно использовать не только с дескрипторами заранее открытых файлов, но и с дескрипторами стандартных устройств. Эти дескрипторы имеют постоянное значение и доступны в любое время функционирования системы: 0 — клавиатура; 1 и 2 — экран; 3 — последовательный порт СОМ1; 4 — параллельный порт LPT1.
Установка текущей файловой позиции
Чтение-запись в файле производятся с текущей файловой позиции, на которую указывает файловый указатель. Функция 42h MS DOS предоставляет гибкие возможности как для начального, так и для текущего позиционирования файлового указателя для последующей операции ввода-вывода.
Вход: АН = 42h; BX = дескриптор файла, полученный при его открытии; AL = начальное положение в файле, относительно которого производится операция чтения-записи (OOh — смещение (беззнаковое значение в CX:DX) от начала файла; O1h — смещение (значение со знаком в CX:DX) от текущей позиции в файле; 02h — смещение (значение со знаком в CX:DX) от конца файла); CX:DX = смещение новой позиции в файле относительно начальной.
Выход: CF = 0 — DX:AX = значение новой позиции в байтах относительно начала файла; CF = 1 — АХ = код ошибки: 1 — неверное значение в AL; 6 — недопустимый дескриптор файла.
Методы позиционирования, заданные величиной в AL, по-разному трактуют значение в паре регистров CX:DX. Метод al = 00 трактует значение в CX:DX как абсолютное. Два других метода (al = 01 и al = 02 ) трактуют содержимое CX:DX как значение со знаком. Необходимо быть внимательным при выполнении операции позиционирования для избежания последующих ошибок при операции чтения-записи. Так, значение в СХ: DX, позиционирующее указатель, может указывать за пределы файла. При этом выделяются два случая:
- значение в СХ: DX указывает на позицию перед началом файла — в этом случае последующая операция чтения-записи будет выполнена с ошибкой;
- значение в СХ:DX указывает на позицию за концом файла — в этом случае последующая операция записи приведет к расширению файла в соответствии со значением в CX:DX.
Примеры использования функции 42h приведем при рассмотрении функций чтения-записи.
Запись в файл или устройство
Запись в файл производится функцией 40h с текущей позиции файлового указателя. Вход: АН = 40 h; ВХ = дескриптор файла; СХ = количество байтов для записи;
DS:DX — указатель на область, из которой записываются данные. Выход: CF = 0 — АХ = число действительно записанных байтов в файл или устройство; CF = 1 — АХ = код ошибки: 5 — в доступе отказано; 6 — недопустимый дескриптор.
Если при вызове функции 40h регистр СХ равен нулю, то данные в файл не записываются и он усекается или расширяется до текущей позиции файлового указателя. Если СХ не равен нулю, то данные в файл записываются начиная с текущей позиции файлового указателя. Операция записи также продвигает файловый указатель на число действительно записанных байтов.
Положение файлового указателя можно изменять явно с помощью функции 42h. Вначале приведем пример программы, выводящей данные на экран.
:prg07_08.asm — программа демонстрации вывода на экран строки функцией 40h.
.data
string db ‘строка для вывода на экран функцией 40h’
len_string=$-stnng point_fname dd string
.
.code
movbx.l -.стандартный дескриптор — экран
mov cx.1en_string
Ids dx.point_fname;формируем указатель на строку string
movah.40h -.номер функции DOS
int 21h ;выводим
jc exit ;переход в случае ошибки
пор -.для тестирования
Далее приведем пример программы, которая заполняет файл my_file.txt данными в виде строк символов, вводимых с клавиатуры. Длина строк — не более 80 символов. Нажатие клавиши Enter после ввода каждой строки означает, что эта строка символов должна являться отдельной строкой файла my_file.txt. Для этого перед выводом каждой строки в файл в конце ее необходимо вставлять символы OdOah.
При нажатии клавиши Пробел в начале ввода очередной строки (ASCII-код — 3210 или 2016) направление ввода данных в файл изменяется следующим образом: файл расширяется на величину, равную количеству уже введенных символов, и дальнейший ввод осуществляется с конца файла. Завершение работы программы определяется моментом, когда оба введенных потока Ь в файле встречаются (не перекрываясь).
:prg07_09.asm — программа заполнения файла my_file.txt данными в виде строк символов.
:вводимыми с клавиатуры.
buf_Oahstruc
len_buf db 83 ;длина buf_0ah
len_in db 0 действительная длина введенного слова (без учета 0dh)
buf_in db 82 dup (20h) :буфер для ввода Сс учетом 0dh и позднее добавляем Oah)
ends
.data
handle dw 0 :дескриптор файла
filename db ‘my_file.txt’,0
point_fname dd filename
buf buf_0ah<>
prev_d label dword ;для сохранения длины предыдущей строки при выводе с конца файла prev dw 0
dw 0
middle dd 0 ;позиция в середине файла, при достижении которой снизу выходим :из программы
.code
:——открываем файл——. —
хогсх.сх ;атрибуты файла — обычный файл
movbx,2 ;режим доступа — доступ для чтения-записи, режим буферизации MS DOS
movdx,12h ;если файл существует, то открыть его без сохранения прежнего содержимого, :в обратном случае создать его
Ids si ,point_fname:формируем указатель на имя файла
movah.6ch ;номер функции DOS
int 21h открываем (создаем) файл
jc exit :если ошибка, то переход на конец ;действия при успешном открытии файла:
mov handle.ax ,-сохраним дескриптор файла ;—позиционируем файловый указатель с начала файла.
mov ah.42h
хог al,al
хог ex,ex
хог dx.dx
mov bx, handle
int 21h cycl: ;вводим очередную строку с клавиатуры
lea dx.buf
mov ah,Oah
Int 21h ;для красоты ввода выводим на экран символ Oah
mov dl .Oah
mov ah.2
int 21h
emp buf.buf_in.20h;первый символ введенной строки сравниваем с пробелом
je revers ;переход на изменение ввода — добавляем Oah в конец введенной строки
lea si.buf.buf_in
mov al .buf .lenjn
cbw push si
add si ,ax
incsi учитываем неучтенный в lenjn символ 0dh
mov byte ptr [si],Oah H—. вывод в файл. —.
I popdx указатель на область, откуда будем выводить строку
mov bx.handle
add ax,2 учитываем неучтенный в len_in символ 0dh
movcx.ax :длина выводимых данных
mov ah.40h
int 21h
jmp cycl
revers: ;записываем файл с конца, предварительно расширив его ;узнаем. сколько было уже записано до этого: ;для этого вначале сбрасываем буферы на диск
mov bx.handle
mov ah.68h
int 21h ;теперь можно и узнать — определение длины файла:
mov al ,2
хог сх.сх
хог dx.dx ;CX:DX -0 — нулевое смещение
mov ah,42h
int 21h :в DX:AX возвращается длина файла в байтах
jc exit :если ошибка :формируем полную длину в edx
shl eax,16
shld edx.eax,16
mov middle.edx сохраним как условие выхода из программы при достижении снизу расширение файла с помощью функции 42h int 21h и последующей записи :умножаем длину на 2. при первой операции записи файл расширится:
shl edx.l
shld ecx.edx.16
mov al.O
хог сх.сх
mov ah.42h
int 21h расширяем файл, устанавливая указатель
jc exit :если ошибка расширим файл, выведя последнюю введенную строку с пробелом:
cycl2: lea si,buf,buf_in
mov al .buf .lenjn
cbw ptush si
add si.ax
incsi учитываем неучтенный в lenjn символ 0dh
добавляем Oah в конец введенной строки
mov byte ptr [si],Oah ;выводим в файл:
popdx указатель на область, откуда будем выводить строку
add ах.2 учитываем неучтенный в len_in символ 0dh
movcx.ax :длина выводимых данных
movprev.ax .сохраним длину для корректировки при выводе следующей строки
mov bx.handle movah.40h int 21h jc exit
;сбрасываем буфер, чтобы смотреть изменения в файле при работе в отладчике -:легче запретить (см. обсуждение ниже)
mov bx,handle
mov ah,68h Int 21h :вводим очередную строку с клавиатуры
lea dx.buf
mov ah.Oah
Int 21h :для красоты ввода выводим на экран символ Oah
mov dl .Oah
mov ah,2
int21h
;. использование 42h с отрицательным смещением относительно
:текущего значения файлового указателя:
устанавливаем файловый указатель в позицию вывода следующей строки
;с учетом того, что выводим с конца (текущей позиции) файла:
хог есх.есх
mov al.buf,len_in
cbw
add prev.ax
add prev.2 учитываем наличие OdOah
sub ecx.prev_d :получаем отрицательное смещение — сформируем его в паре СХ:DX
shrd edx,ecx,16
shr edx.16 :довернем edx
shr ecx.16 :и есх устанавливаем файловую позицию для записи очередной строки
mov bx,handle
mov ah.42h
moval.l ;смещение от текущей позиции
int 21h :сравним текущую позицию с middle
shl eax.16
shld edx,eax,16
cmp edx,middle
jl exit
jmp cycl2 exit:
Программа выглядит не очень эстетично, но главная ее цель достигнута — показать работу с файловым указателем при записи в файл. Мы попробовали разные варианты: позиционирование на конец файла (при этом можно узнать длину файла); использование отрицательного смещения (задавая нулевое значение в CX:DX при al = 1 можно получить в DX:AX текущую позицию в файле); расширение файла путем задания в СХ: DX заведомо большего значения, чем длина файла. Как видно из программы выше, все эти и другие эффекты достигаются за счет манипулирования значениями в парах СХ :DX и DX:AX, а также в регистре AL, где задается начальное положение в файле, относительно которого производится операция чтения-записи.
В заключение рассмотрения функции 40h записи в файл отметим то, для чего мы использовали функцию сброса буферов на диск 68h. Для этого коротко необходимо коснуться проблемы буферизации ввода-вывода в MS DOS. MS DOS ис-
пользует буферизацию ввода-вывода для ускорения работы с диском. При этом, р частности, данные, записываемые на диск, не записываются на него сразу, а по-щешаются вначале в буфер. Запись буфера на диск производится при его заполнении- Буферизацию эффективно использовать при интенсивной работе с одними и теми же данными.
Тогда при необходимости чтения данных с диска они 6уДУт читаться из буфера. В нашей программе буферизация нам только мешала, так как при работе в отладчике мы не могли своевременно наблюдать за изменениями выходного файла my_file.txt Для этого нам приходилось использовать функцию 68h для принудительного сохранения буферов на диск. Вход: АН = 68h; BX = дескриптор файла. Выход: CF = 0 в случае успеха; CF = 1 — АХ = код ошибки.
В результате работы функции все данные из буферов дисков DOS немедленно записываются на диск, при этом модифицируется соответствующий файлу
элемент каталога.
Для нашей задачи буферизацию лучше вовсе запретить, тогда отпадет необходимость в принудительном сохранении строк в файле для того, чтобы в динамике отслеживать его изменения. Для этого при вызове функции 6ch в регистре ВН необходимо установить бит 6 следующим образом: 6 = 0 — использовать стандартную для MS DOS буферизацию; 6 = 1 — отменить стандартную для MS DOS буферизацию. В нашем примере это можно сделать так:
. открываем файл————————————-
хогсх.сх атрибуты файла — обычный файл
mov bx.4002h :режим доступа — доступ для чтения-записи, запрет буферизации
movdx,12h :если файл существует, то открыть его без сохранения прежнего
содержимого, в обратном случае создать файл Ids si.point_fname:формируем указатель на имя файла movah.6ch :номер функции DOS int21h открываем (создаем) файл jc exit :если ошибка, то переход на конец
—————————————————————————
Все вызовы функции 68h в приведенной выше программе можно закомментировать.
Чтение из файла или устройства
Чтение из файла в область памяти осуществляется функцией 3Fh.
Вход: АН = 3Fh; BX = дескриптор файла; СХ = количество байтов для чтения; DS-.DX — указатель на область памяти, в которую помещаются прочитанные байты. Выход: CF = 0 — АХ = число действительно прочитанных байтов из файла; CF = 1 — АХ = код ошибки: 5 — в доступе отказано; 6 — недопустимый дескриптор.
Чтение данных производится начиная с текущей позиции в файле, которая после успешного чтения смещается на значение, равное количеству прочитанных байтов. Если в качестве файла используется стандартная консоль (клавиатура), то чтение производится до первого символа CR (carriage return) с кодом 0dh, соответствующего нажатию клавиши Enter.
Это, кстати, еще один способ ввода » Данных с клавиатуры в программу. Кроме символов введенной строки в ее конец помещаются символы 0dh и Oah. Это необходимо учитывать при задании размера буфера для ввода. Способ ввода данных с экрана с помощью функции 3Fh . стрирует приведенный ниже пример программы.
:prg07_10.asm — программа демонстрации ввода данных с экрана с помощью функции 3Fh.
.data
string db 80 dup Г «) 1en_string=$-string point_fname dd string
.code
. вводим с экрана. ——. —————-
movbx.O стандартный дескриптор — клавиатура
mov cx.len_string
Ids dx.point_fname:формируем указатель на строку string
movah,3fh ;номер функции DOS
int 21h
jc exit :переход в случае ошибки ;———выводим на экран———————.
movbx.l стандартный дескриптор — экран :две строки ниже в данном случае можно опустить
mov ex.len_string
Ids dx.point_fname;0opMnpyeM указатель на строку string
movah.40h ;номер функции DOS
int 21h открываем файл
jc exit :переход в случае ошибки
Для демонстрации работы функции с дисковым файлом приведем программу чтения и вывода на экран содержимого файла, имя которого вводится в командной строке. Побочная цель этой программы — научиться обрабатывать в программе командную строку DOS. Поясним последний момент.
Содержимое командной строки, следующее за именем программы при ее вызове и называемое хвостом команды, помещается в префикс программного сегмента (PSP) со смещением 80h от его начала и максимально имеет размер 128 байт. Первый байт этой области содержит длину хвоста команды, а первый символ хвоста, при его наличии, располагается со смещением 81h от начала PSP. Последний символ хвоста команды — всегда 0dh. Начало PSP найти очень легко — когда программа загружается в память для исполнения, то загрузчик устанавливает регистры ES и DS равными адресу PSP.
He забывайте после определения размера файла возвращать файловый указатель в нужное место файла.
Источник: programm.ws