В. Ю. Наумов Введение в информатику
Скачать 2.05 Mb.
|
fseek. Среди основных алгоритмов обработки файлов стоит особо отметить алгоритм чтения компонент и записи на это место нового значения. Поскольку указатель перемещается вправо как при чтении, так и при записи, то сразу после записи его следует вернуть на одну позицию назад. Приведем пример последовательного чтения компонент файла с последующей записью новых на то же место. Для этого обычно применяют алгоритм, подобный изображенному на рис. 7.10. Далее рассмотрим задачу с использованием описанного алгоритма. ПРИМЕР Ввести файл действительных чисел. Найти в нем среднее арифметическое каждой второй компоненты. Далее все компоненты, чей модуль меньше модуля найденного среднего арифметического, удвоить. Файл до и после преобразования вывести на экран. Начинаем, как обычно, с тестового примера: fread(&a, sizeof(a),1,f) f=fopen( “1.txt”,”r”) Выполнение действий над а fseek (f, -sizeof(a), SEEK_CUR) fwrite(&a, sizeof(a),1,f) Рис. 7.8. Формирование новых компонент на основе прочитанных 226 -2 3 8 1 -10 -3 0 9 -16 0 20 11 0 1 2 3 4 5 6 7 8 9 10 11 12 F = SrA = (3 + 1+ 9 + (-3) + 0 +11)/6 = 21/6 = 3.5 Входные данные: Выходные данные: -4 6 8 2 -10 -6 0 9 -16 0 20 11 0 1 2 3 4 5 6 7 8 9 10 11 12 F = Компоненты, удовлетворяющие условию |a| < |3.5| Следующий этап – составление блок-схемы алгоритма. Главная программа Начало Ввод файла F Конец Подсчет SrA ( ср. арифметического) Вывод измененного файла F Вывод файла F Изменение файла F 1 2 3 4 5 6 а) Шаг 3-4 fread(&a, sizeof(a),1,f) б) k = 0 SrA = 0 fseek(f,sizeof(a), SEEK_SET) SrA = SrA + a k ++ fseek(f,sizeof(a), SEEK_CUR) SrA = SrA/k Рис. 7.9. Главная программа (а) и поиск среднего арифметического каждой второй компоненты (б) 227 Можно сначала записать общий алгоритм, а далее каждый шаг детализировать. Общая последовательность действий решения представлена на рис. 7.11, а. Здесь шаги 1-2, 2-3 и 5-6 уже были описаны ранее (см. рис. 7.4, 7.5). Потому есть смысл детализировать шаги 3-4 и 4-5. Их детализация изображена на рис. 7.11, б и 7.12. Следует отметить, что доступ к каждой второй компоненте файла осуществляется прямым обращением. Из детализации шага 3-4 видно, что есть возможность «проскочить» конец файла и поставить указатель на несуществующую позицию. Однако это не вызовет ошибки, поскольку такие действия возможны. Даже можно записать значение в компоненту далеко за границей файла (если файл открыт с возможностью добавления компонент). fread(&a, sizeof(a),1,f) f=fopen( “1.txt”,”r+”) |a| < |SrA| fseek(f,-sizeof(a), SEEK_CUR) Шаг 4-5 a = 2*a fwrite(&a, sizeof(a),1,f) Рис. 7.10. Удвоение компонент, удовлетворяющих условию 228 По составленным блок-схемам можно написать программу: #include #include { FILE *f; f=fopen("1.txt","w"); cout<<"\nInput file:"; int i=0,a; cout<<"\nInput first element or 999 for exit"; cin>>a; while (a!=999) { fwrite(&a,sizeof(a),1,f); i++; cout<<"\nInput "<>a; } fclose(f); f=fopen("1.txt","r"); cout<<"\nFile f:"; while (fread(&a,sizeof(a),1,f)) { cout<<"\t"< } fclose(f); f=fopen("1.dat","r"); float sra=0,k=0; fseek(f,sizeof(a),SEEK_SET); while (fread(&a,sizeof(a),1,f)) { sra=sra+a; k++; fseek(f,sizeof(a),SEEK_CUR); } sra=sra/k; fclose(f); f=fopen("1.dat","r+"); while (fread(&a,sizeof(a),1,f)) { if (fabs(a) < fabs(sra)) { a*=2; fseek(f, -sizeof(a), SEEK_CUR); fwrite(&a,sizeof(a),1,f); } } fclose(f); f=fopen("1.txt","r"); cout<<"\nFile f:"; 230 k -- f=fopen( “1.txt”,”r+”) + sort = false i=1; i fseek(F, -2sizeof(a), SEEK_CUR) fwrite(&a2, sizeof(a),1,f) sort = true rewind(f) fread(&a, sizeof(a),1,f) a = a2 fread(&a2, sizeof(a),1,f) fwrite(&a, sizeof(a),1,f) sort - k := filesize( “1.txt”)/ sizeof (a) Рис. 7.12. Сортировка файла по возрастанию методом «пузырька» 231 Здесь функция filesize (char *filename) возвращает размер закрытого файла char *filename в байтах. Для определения количества компонентов нужно разделить полученное значение на размер одного компонента. 7.5. Файлы и функции Использование файлов в качестве формальных параметров функций допускается только как параметров-переменных, так как в функцию передается указатель, связанный с файлом. Передача по значению неосуществима ввиду возможной относительной неограниченности размера файла. Рассмотрим работу с файлами и функциями на примере решения следующей задачи. ПРИМЕР Создать файл F. Читая файл с конца, переписать компоненты с четных позиций в файл G, а с нечетных – в файл H. Начнем с тестового примера: 1 3 0 -5 -8 1 2 9 31 0 0 1 2 3 4 5 6 7 8 9 10 F = 0 1 9 -5 3 0 1 2 3 4 5 H = 31 -8 2 0 1 0 1 2 3 4 5 G = Выходные данные: Входные данные: Задачу решим, используя функции. Блок-схема алгоритма основной программы представлена на рис. 7.15. 232 Теперь опишем все функции, входящие в данный алгоритм. Это, прежде всего, ввод и вывод (InputFile и OutputFile) – рис. 7.16, а также FormNewFile (рис. 7.17). В функции FormNewFile следует обратить внимание на то, что проход происходит в обратном направлении. Факт достижения начала файла проверяется постусловием стандартной функцией ftell. Цикл прекращается при равенстве нулю текущей позиции файла. Начало Конец InputFile(F, ‘f.txt’) FormNewFiles(F, G, H) OutputFile(F, “f.txt”) OutputFile(G, “g.txt”) OutputFile(H, “h.txt”) Рис. 7.13. Основная программа к задаче на чтение файла в обратном порядке 233 Имея блок-схему, достаточно просто составить программу. #include #include { x=fopen(FileName, "w"); cout<<"\nInput file:"; int i=0,a; cout<<"\nInput first element or 999 for exit"; cin>>a; while (a!=999) { fwrite(&a,sizeof(a),1,x); i++; cout<<"\nInput "<>a; } fclose(x); } procedure InputFile(FILE *x, char *FileName) Выход а ≠ 999 x=fopen(FileName, "w") Ввод а (1-я компонента файла) fwrite(&a, sizeof(a),1,x) i:= i+1 Ввод а б) fread(&a, sizeof(a),1,x) x=fopen(FileName, "r") Вывод a Выход procedure OutputFile(FILE *x, char *FileName) fclose(x) fclose(x) а) Рис. 7.14. Процедуры ввода и вывода файла 234 void FormNewFile (FILE *f, FILE *g, FILE *h) Выход f=fopen( “f.txt”,”r”) fclose(f) fclose(g) fclose(h) fseek(f, -sizeof(a), SEEK_END) g=fopen( “g.txt”,”w”) h=fopen( “h.txt”,”w”) fread(&a, sizeof(a),1,f) i % 2 == 0 fwrite(&a, sizeof(a),1,g) fwrite(&a, sizeof(a),1,h) i = i - 1 + i=ftell(f)/sizeof(a) fseek(f, i*sizeof(a), SEEK_SET) - i>=0 Рис. 7.15. Процедура чтения файла в обратном порядке и записи результатов в пару новых файлов 235 void OutputFile(FILE *x, char *FileName) { int a; x=fopen(FileName,"r"); cout<<"\nFile "< } void FormNewFile (FILE *f, FILE *g, FILE *h) { int a; f=fopen("f.txt","r"); fseek(f, -sizeof(a), SEEK_END); int i=ftell(f)/sizeof(a); g=fopen("g.txt","w"); h=fopen("h.txt","w"); do { fread(&a, sizeof(a),1,f); if (i % 2 == 0) fwrite(&a, sizeof(a),1,g); else fwrite(&a, sizeof(a),1,h); i--; fseek(f,i*sizeof(a),SEEK_SET); } while(i>=0); fclose(f); fclose(g); fclose(h); } int main() { FILE *f; FILE *g; FILE *h; InputFile(f,"f.txt"); OutputFile(f,"f.txt"); FormNewFile(f,g,h); OutputFile(g,"g.txt"); OutputFile(h,"h.txt"); } 236 7.6. Файлы и массивы Использование файлов является универсальным инструментом для хранения в энергонезависимой памяти информации любого вида. Как наиболее простой, и в то же время наглядный пример этой информации, можно рассмотреть массивы. Важно придумать правило, по которому будет вестись запись компонент в файл и, соответственно, правило извлечения данных в таком порядке, чтобы на выходе получалась структура, идентичная структуре на входе. Для начала возьмем одномерный массив. Вспомним, что компонентный файл является весьма схожим с одномерным массивом практически по всем параметрам, потому самый простой вариант действий в данном случае – ничего особо не менять. Просто следует поставить в соответствие компонентам файла элементы массива. Нужно указать в файле начальную позицию, с которой будут вестись чтение/запись массива и число элементов в массиве. Фрагмент программы, поэлементно копирующий файл F в одномерный массив X, может быть таким: f=fopen(“1.txt”,”r”); i=0; while (fread(&a,sizeof(a),1,f)) { x[i]=a; i++; } Или, наоборот, копирование элементов массива в файл: FILE *f=fopen(“1.txt”,”w”); for (int i=0; i } Однако проще записать массив в файл целиком: fwrite(x,sizeof(x),1,f); 237 Для двумерных массивов можно придумать разные способы переноса элементов в файл. Самый простой – чтение матрицы по столбцам или по строкам. Например, так можно копировать элементы двумерного массива A в файл F построчно: fwrite(a,sizeof(a),1,f); а так по столбцам: FILE *f=fopen(“1.txt”,”w”); for (int i=0; i } Обратная операция представляется более сложной ввиду гипотетической невозможности формирования двумерного массива из элементов файла. Если длина файла не равна произведению строк и столбцов матрицы, то при ее формировании возникает множество вопросов: «А действительно ли в файле содержится матрица?», «В какой именно части файла она содержится?», «Может можно сформировать матрицу только из начальных элементов матрицы?». Рассмотрим случай формирования матрицы A размером N×M из файла F: f=fopen(“1.txt”,”r”); fseek(f,0L,SEEK_END); int buf,fn=ftell(f)/sizeof(a); fseek(f,0L,SEEK_SET); if (fn >= n*m) { for (int i=0; i } } else cout<<”\nNot enough components in the file F”; Еще один пример: переписать из двумерного массива A в файл F элементы в последовательности, изображенной ниже, т. е. «по змейке». 238 15 1 96 52 -8 -45 23 36 12 -56 0 2 41 70 0 -10 A = В результате получается файл F: j=0; j Выход f=fopen( “1.txt”,”w”) fclose(f) j % 2 ≠ 0 i=n-1; i>=0; i-- fwrite(&buf, sizeof(buf),1,f) i=0; i buf = A[i][j] buf = A[i][j] Рис. 7.16. Формирование файла из матрицы «по змейке» 239 15 96 -8 23 1 52 -45 36 12 0 41 0 -56 2 70 -10 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Не приводя стандартных процедур ввода/вывода матрицы и вывода файла, продемонстрируем процедуру собственно формирования файла, изображенную на рис. 7.18. Рассмотрим полное решение достаточно сложной задачи на совместное использование файлов и массивов. ПРИМЕР Задан файл F, в котором содержатся элементы матрицы. Причем известно, что сначала записано число строк, далее – число столбцов и элементы матрицы построчно. Нужно: 1) восстановить матрицу; 2) отнормировать ее (разделить все элементы на значение максимального); 3) занести матрицу обратно в файл; 4) переписать все положительные элементы из файла в одномерный массив B. Тестовый пример показан на рис. 7.19. Блок-схемы основной программы и всех процедур представлены на рис. 7.20 – 7.23. 15 1 50 -34 -3 -36 12 -9 0 2 41 -10 3 4 15 -34 50 1 12 2 0 -9 -3 -36 41 -10 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 0.30 0.02 1.00 -0.68 -0.06 -0.72 0.24 -0.18 0.00 0.04 0.82 -0.20 3.00 4.00 0.30 -0.68 1.00 0.02 0.24 0.04 0.00 -0.18 -0.06 -0.72 0.82 -0.20 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 3.00 4.00 0.30 1.00 0.02 0.24 0.04 0.82 Исходный файл F: Матрица до нормировки: Матрица после нормировки: Max = 50 Нормированная матрица, занесенная обратно в файл F: Вектор B: Рис. 7.17 Тестовый пример к задаче 240 Начало Конец InputFile(f, “f.txt”) OutputFile(f, “f.txt”) FormMatrix(f, a, na, ma) OutputMatrix(a, na, ma) OutputVector(b, k) NormMatrix(a, na, ma) OutputMatrix(a, na, ma) OutputFile(f, “f.txt”) FormVector(f, b, k) а) б) (n<1) || (m<1) x=fopen(name, ”w”) Ввод n, m Выход void InputFile(FILE *x, char *name) fclose(x) Ввод n, m i=0; i fwrite(&buf, sizeof(buf),1,x) Ввод buf fwrite(&m, sizeof(m),1,x) FormFile(f,a,na,ma) Рис. 7.18 Главная программа (а) и – формирование файла (б) 241 fread(&buf, sizeof(buf),1,x) x=fopen(name, ”r”) Вывод buf Выход void OutputFile(FILE *x, char *name) fclose(x) j=0; j i=0; i Вывод matr[i][j] j=0; j “f.txt”,”r”) matr[i][j]=buf void FormMatrix(FILE *x, float matr[10][15], int &n, int &m) i=0; i fread(&buf, sizeof(buf),1,x) fclose(x) Выход fread(&m, sizeof(m),1,x) а) б) в) Рис. 7.19. Функция вывода файла (а); функция формирования матрицы из файла (б); и функция вывода матрицы (в) 242 j=0; j (float matr[10][15], int n, int m) i=0; i j=0; j matr[i][j] = matr[i][j]/max j=0; j “f.txt”,”w”) fwrite(&buf,sizeof(buf), 1,x) procedure FormFile (FILE *x, const float matr[10][15], int n, int m) i=0; i buf = matr[i][j] fclose(X) Выход б) fwrite(&m, sizeof(m),1,x) а) Рис. 7.20. Нормировка матрицы (а) и запись матрицы в файл (б) 243 По блок-схеме составим программу. #include #include { float n,m; x=fopen(name,"w"); cout<<"\nInput n,m"; cin>>n>>m; while (n<1 || m<1) { cout<<"\nInput n,m"; cin>>n>>m; } fread(&buf, sizeof(buf),1,x) x=fopen( “f.txt”,”r”) Выход void FormVector(FILE *x, float vect[150], int &i) fclose(x) void OutputVector (const float vect[150], int k) i=0; i Вывод vect[ i ] i = 0 buf > 0 i++ vect[ i ] = buf б) а) Рис. 7.21. Формирование вектора (а) и вывод вектора (б) 244 fwrite(&n,sizeof(n),1,x); fwrite(&m,sizeof(m),1,x); for (int i=0; i } fclose(x); } void OutputFile(FILE *x, char *name) { float a; x=fopen(name,"r"); cout<<"\nFile "< } void OutputMatrix(const float matr[10][15],int n, int m) { cout<<"\nmatrix :\n"; for (int i=0; i } void FormMatrix(FILE *x, float matr[10][15], int &n, int &m) { x=fopen("f.txt","r"); float buf; fread(&buf, sizeof(buf), 1 , x); n=buf; fread(&buf, sizeof(buf), 1 , x); m=buf; for (int i=0; i } fclose(x); } void NormMatrix(float matr[10][15], int n, int m) { 245 float max=matr[0][0]; for (int i=0; i { x=fopen("f.txt","r"); i=0; float buf; while (fread(&buf, sizeof(buf),1,x)) if (buf>0) { vect[i]=buf; i++; } fclose(x); } void OutputVector(const float vect[150], int k) { cout<<"\nVecotr :"; for (int i=0; i { x=fopen("f.txt","w"); float buf; buf=n; fwrite(&buf,sizeof(buf),1,x); buf=m; fwrite(&buf,sizeof(buf),1,x); for (int i=0; i } fclose(x); } int main() { FILE *f; float a[10][15],b[150]; int na,ma,k; InputFile(f, "f.txt"); 246 OutputFile(f, "f.txt"); FormMatrix(f,a,na,ma); OutputMatrix(a,na,ma); NormMatrix(a,na,ma); OutputMatrix(a,na,ma); FormFile(f,a,na,ma); OutputFile(f, "f.txt"); FormVector(f,b,k); OutputVector(b,k); } 7.7. Работа с файлами в С++ В С++ есть более удобные способы работы с файлами по сравнению с Си. Далее будет приведена краткая справочная информация по организации работы с файлами в С++. Основной главы про файлы послужила работа с ними в нотации языка Си, потому что для работы с файлами в С++ используются классы, объекты, методы и прочие понятия объектно-ориентированного программирования, описание которых не вошло в это учебное пособие. В С++ для работы с файлами необходимо подключить заголовочный файл Файловый ввод/вывод аналогичен стандартному вводу/выводу, единственное отличие – это то, что ввод/вывод выполнятся не на экран, а в файл. Если ввод/вывод на стандартные устройства выполняется с помощью объектов cin и cout, то для организации файлового ввода/вывода достаточно создать собственные объекты, которые можно использовать аналогично операторам cin и cout. Чтобы сделать запись в файл нужно создать объект класса ofstream. 247 ПРИМЕР Записать в файл строку «Работа с файлами в С++. #include { // создаём объект класса ofstream для записи и связываем // его с файлом f.txt ofstream fout("f.txt"); // запись строки в файл fout << "Работа с файлами в С++"; // закрываем файл fout.close(); } Чтобы считать данные из файла, нужно создать объект класса ifstream. ПРИМЕР Записать в файл строку «Работа с файлами в С++. #include #include { // буфер хранения считываемого из файла текста char buff[50]; // открыли файл для чтения ifstream fin("cppstudio.txt"); // считали первое слово из файла fin >> buff; // напечатали это слово cout << buff << endl; // считали строку из файла fin.getline(buff, 50); // закрываем файл fin.close(); // напечатали эту строку cout << buff << endl; } В программе показаны два способа чтения из файла, первый – ис- пользуя операцию передачи в поток, второй – используя функцию getline() . В первом случае считывается только первое слово, а во вто- ром случае считывается строка, длинной 50 символов. 248 БИБЛИОГРАФИЧЕСКИЙ СПИСОК 1. Наумов, В.Ю. Информатика и программирование: руководство к лабораторным и практическим занятиям по С++. Часть 1: учеб.пособие / В.Ю. Наумов, О.А. Авдеюк, Л.Г. Акулов, О.В. Гостевская, И.Г. Лемешки- на. - Волгоград, 2014. – 80 с. 2. Наумов, В.Ю. Информатика и программирование: руководство к лабораторным и практическим занятиям по С++. Часть 2: учеб.пособие / В.Ю. Наумов, О.А. Авдеюк, Л.Г. Акулов, О.В. Гостевская, И.Г. Лемеш- кина. - Волгоград, 2014. – 64 с. 3. Наумов, В. Ю. Информатика. Сборник заданий для лабораторных работ по информатике. Учеб. пособие / В.Ю. Наумов, О.В. Гостевская, И. Г. Лемешкина, Е.С. Павлова, Р.С. Богатырёв, Л. Г. Акулов, Р.В. Литовкин, О.А. Авдеюк. ВолгГТУ. – Волгоград, 2010. – 80 с. 4. Наумов, В. Ю. Сборник заданий для семестровых работ по инфор- матике. Часть II: учеб. пособие / В. Ю. Наумов, И. Г. Лемешкина, Е. С. Павлова, Р. С. Богатырев, Л. Г. Акулов, Р. В. Литовкин, О. А. Авдеюк. – Волгоград: ИУНЛ ВолгГТУ, 2011. – 64 с. 5. Викентьева, О. Л. Конспект лекций по курсу «Алгоритмические языки и программирование». Учеб. пособие / О. Л. Викентьева. – Пермь, 2003. – 82 с. |