Sign in Join
Wiki copytemporary

File Handling In Pascal/ru: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: File Handling In Pascal/ru

OldNewLine
1+## Введение
2+
3+Каждый программист должен знать, как работать с файлами. Файлы используются для сохранения данных, т. е. в качестве хранилища данных, таким образом, что они могут быть получены в любой момент, без их воссоздания. Файлы можно использовать для сохранения пользовательских настроек, журналов ошибок, измерение или вычисление результатов, и многое другое. В данном разделе описываются основы использования файлов.
4+
5+## Процедурный стиль
6+
7+Это довольно старый стиль, использующейся ещё во времена, когда Pascal не был объектно-ориентированным языком. Суть его в том, что задается тип файла, определяющий, какие будут храниться в нем данные. Для этого, используется конструкция вида: **file of** *\<тип данных\>*, где *\<тип данных\>* - название типа, который хранит в себе файл. Помимо стандартных типов (integer, extended, char и т.д.), существует особый тип - TextFile. Он определят, что каждая строка заканчивается специальным(ми) символом(ами) конца строки (См. [[End of Line/ru|LineEnding]]). Эти файлы могут быть открыты и отредактированы внутри среды Lazarus или в любом другом текстовом редакторе.
8+
9+Ниже представлены примеры создания собственных типов файлов:
10+
11+``` pascal
12+...
13+type
14+ TIntegerFile = file of integer; // Позволяет писать только целые числа в файл
15+ TExtendedFile = file of extended; // Позволяет писать только дробные цифры в файл
16+ TCharFile = file of char; // Позволяет писать только одиночные символы в файл
17+```
18+
19+### Обработка ошибок ввода/вывода
20+
21+Параметр компилятора [обработки ошибок ввода/вывода](http://www.freepascal.org/docs-html/prog/progsu38.html#x45-440001.2.38) указывает, как должна вести себя программа при возникновении ошибки в процессе работы с файлами: вызвать исключение или хранить результат операции ввода/вывода в специальной переменной IOResult.
22+
23+Это задаётся с помощью специальной директивы компилятора:
24+
25+``` pascal
26+{$I+} // В случаи ошибки будет вызвано исключение EInOutError (по умолчанию)
27+{$I-} // Подавлять ошибки ввода-вывода: проверьте переменную IOResult для получения кода ошибки.
28+```
29+
30+В случаи подавления ошибок ввода-вывода ({\$I-}) результат операции с файлом будет храниться в переменной IOResult типа [[Cardinal|cardinal (числовой тип)]]. Каждое число, хранимое в IOResult определяет тип возникшей ошибки(подробнее: [1](http://www.freepascal.org/docs-html/rtl/system/ioresult.html)).
31+
32+### Процедуры работы с файлами
33+
34+Эти процедуры и функции находятся в модуле system. Для более подробной информации смотрите документацию FPC:
35+
36+[`ссылка на модуль 'System'`](http://www.freepascal.org/docs-html/rtl/system/index-5.html)`.`
37+
38+- **AssignFile** (не допускайте использование процедуры **Assign**) - Связывает переменную с файлом
39+- **Append** - Открывает существующий файл для записи данных в конец и их редактирования
40+- **BlockRead** - Чтение данных из не типизированного файла в память
41+- **BlockWrite** - Запись данных из памяти в не типизированный файл
42+- **CloseFile** (не допускайте использование процедуры **Close**) - Закрыть открытый файл
43+- **EOF** - Проверка наличия конца файла
44+- **Erase** - Стереть файл с диска
45+- **FilePos** - Получить позицию в файле
46+- **FileSize** - Получить размер файла
47+- **Flush** - Записать файловый буфер на диск
48+- **IOResult** - Возвращает результат последней операции ввода\вывода
49+- **Read** - Считать из текстового файла
50+- **ReadLn** - Считать из текстового файла и перейти к следующей строке
51+- **Reset** - Открыть файл для чтения
52+- **Rewrite** - Создать и открыть файл для записи
53+- **Seek** - Изменить позицию в файле
54+- **SeekEOF** - Переместить позицию в файле в его конец
55+- **SeekEOLn** - Переместить позицию в файле в конец строки
56+- **Truncate** - Удалить все данные, после текущей позиции
57+- **Write** - Записать переменную в файл
58+- **WriteLn** - Записать переменную в текстовый файл и перейти к новой строке
59+
60+### Пример
61+
62+Пример работы с текстовым файлом (тип TextFile):
63+
64+``` pascal
65+program CreateFile;
66+
67+uses
68+ Sysutils;
69+
70+const
71+ C_FNAME = 'textfile.txt';
72+
73+var
74+ tfOut: TextFile;
75+
76+begin
77+ // Связываем имя файла с переменной
78+ AssignFile(tfOut, C_FNAME);
79+
80+ // Использовать исключение для перехвата ошибок (это по умолчанию и указывать не обязательно)
81+ {$I+}
82+
83+ // Для обработки исключений, используем блок try/except
84+ try
85+ // Создать файл, записать текст и закрыть его.
86+ rewrite(tfOut);
87+
88+ writeln(tfOut, 'Пример текстового файла!');
89+ writeln(tfOut, 'Пример записи числа: ', 42);
90+
91+ CloseFile(tfOut);
92+
93+ except
94+ // Если ошибка - отобразить её
95+ on E: EInOutError do
96+ writeln('Ошибка обработки файла. Детали: ', E.ClassName, '/', E.Message);
97+ end;
98+
99+ // Выводим результат операции и ожидаем нажатие Enter
100+ writeln('Файл ', C_FNAME, ' создан. Нажмите ВВОД для выхода.');
101+ readln;
102+end.
103+```
104+
105+Теперь откройте файл в любом текстовом редакторе и вы увидите, что пример текста, записан в нем! Вы можете проверить работу обработки ошибок, установив атрибут файла "только для чтения" и снова запустив программу.
106+
107+Обратите внимание, что в примере используется блок try/except. Данный способ позволяет выполнять несколько операций с файлами и использовать обработку исключений. Вы также можете использовать режим {\$I-}, но тогда вам придется проверять переменную IOResult после каждой операции с файлами для контроля ошибок.
108+
109+Ниже приведен пример записи текста в конец файла:
110+
111+``` pascal
112+program AppendToFile;
113+
114+uses
115+ Sysutils;
116+
117+const
118+ C_FNAME = 'textfile.txt';
119+
120+var
121+ tfOut: TextFile;
122+
123+begin
124+ // Связываем имя файла с переменной
125+ AssignFile(tfOut, C_FNAME);
126+
127+ // Для обработки исключений, используем блок try/except
128+ try
129+ // Открыть файл для записи в конец, записать текст и закрыть его.
130+ append(tfOut);
131+
132+ writeln(tfOut, 'Ещё пример текстового файла!');
133+ writeln(tfOut, 'Результат 6 * 7 = ', 6 * 7);
134+
135+ CloseFile(tfOut);
136+
137+ except
138+ on E: EInOutError do
139+ writeln('Ошибка обработки файла. Детали: ', E.Message);
140+ end;
141+
142+ // Выводим результат операции и ожидаем нажатие Enter
143+ writeln('Файл ', C_FNAME, ' возможно содержит больше текста. Нажмите ВВОД для выхода.');
144+ readln;
145+end.
146+```
147+
148+Чтение текстового файла:
149+
150+``` pascal
151+program ReadFile;
152+
153+uses
154+ Sysutils;
155+
156+const
157+ C_FNAME = 'textfile.txt';
158+
159+var
160+ tfIn: TextFile;
161+ s: string;
162+
163+begin
164+ // Вывод некой информации
165+ writeln('Чтение содержимого файла: ', C_FNAME);
166+ writeln('=========================================');
167+
168+ // Связываем имя файла с переменной
169+ AssignFile(tfIn, C_FNAME);
170+
171+ // Для обработки исключений, используем блок try/except
172+ try
173+ // Открыть файл для чтения
174+ reset(tfIn);
175+
176+ // Считываем строки, пока не закончится файл
177+ while not eof(tfIn) do
178+ begin
179+ readln(tfIn, s);
180+ writeln(s);
181+ end;
182+
183+ // Готово. Закрываем файл.
184+ CloseFile(tfIn);
185+
186+ except
187+ on E: EInOutError do
188+ writeln('Ошибка обработки файла. Детали: ', E.Message);
189+ end;
190+
191+ // Выводим результат операции и ожидаем нажатие Enter
192+ writeln('=========================================');
193+ writeln('Файл ', C_FNAME, ' считан. Нажмите ВВОД для выхода.');
194+ readln;
195+end.
196+```
197+
198+## Объектный стиль
199+
200+В дополнение к старому методу обработки файлов, упомянутому выше процедурному стилю, существует новая система, которая использует концепции потоков (данных) на более высоком уровне абстракции. Это означает, что данные могут считываться или записываться в любом месте (диск, память, аппаратные порты и т. д.) через один универсальный интерфейс.
201+
202+Кроме того, большинство классов обработки строк, могут иметь возможность загружать/сохранять содержимое из/в файл. Эти методы обычно называются SaveToFile и LoadFromFile.
203+
204+### Двоичные файлы
205+
206+Для прямого доступа к файлам, так же удобно использовать класс TFileStream. Этот класс представляет собой инкапсуляцию системных процедур FileOpen, FileCreate, FileRead, FileWrite, FileSeek и FileClose, расположенных в модуле SysUtils.
207+
208+[Процедуры ввода-вывода](http://www.freepascal.org/docs-html/rtl/sysutils/ioroutines.html)
209+
210+В приведенном ниже примере обратите внимание, что обработка действий с файлом, расположена внутри блока try..except. Поэтому обработка ошибок происходит так же, как в случаи использования процедур работы с файлами в классическом Pascal.
211+
212+``` pascal
213+program WriteBinaryData;
214+{$mode objfpc}
215+
216+uses
217+ Classes, Sysutils;
218+
219+const
220+ C_FNAME = 'binarydata.bin';
221+
222+var
223+ fsOut : TFileStream;
224+ ChrBuffer: array[0..2] of char;
225+
226+begin
227+ // Создать некоторые случайные данные, которые будут храниться в файле
228+ ChrBuffer[0] := 'A';
229+ ChrBuffer[1] := 'B';
230+ ChrBuffer[2] := 'C';
231+
232+ // Перехват ошибок в случае, если файл не может быть создан
233+ try
234+ // Создать экземпляр потока файла, записать в него данные и уничтожить его, чтобы предотвратить утечки памяти
235+ fsOut := TFileStream.Create( C_FNAME, fmCreate);
236+ fsOut.Write(ChrBuffer, sizeof(ChrBuffer));
237+ fsOut.Free;
238+
239+ // Обработка ошибки
240+ except
241+ on E:Exception do
242+ writeln('Файл ', C_FNAME, ' не создан, так как: ', E.Message);
243+ end;
244+
245+ // Выводим результат операции
246+ writeln('Файл ', C_FNAME, ' создан. Нажмите ВВОД для выхода.');
247+ //Ожидаем нажатие Enter
248+ readln;
249+end.
250+```
251+
252+Вы так же можете загрузить весь файл в память, если его размер существенно меньше, чем имеющийся системной памяти. Если файл превышает размер системной памяти, то ваша операционная система начнет использовать файл подкачки, снижая скорость работы с файлом.
253+
254+``` pascal
255+program ReadBinaryDataInMemoryForAppend;
256+{$mode objfpc}
257+
258+uses
259+ Classes, Sysutils;
260+
261+const
262+ C_FNAME = 'binarydata.bin';
263+
264+var
265+ msApp: TMemoryStream;
266+
267+begin
268+ // Создаем поток в памяти
269+ msApp := TMemoryStream.Create;
270+
271+ // Перехват ошибок в случае их возникновения
272+ try
273+ // Считать данные в память
274+ msApp.LoadFromFile(C_FNAME);
275+
276+ // Переходим в конец данных
277+ msApp.Seek(0, soEnd);
278+
279+ // Запись неких данных в поток
280+ msApp.WriteByte(68);
281+ msApp.WriteAnsiString('Некий текст');
282+ msApp.WriteDWord(671202);
283+
284+ // Запись данных на диск (файл будет перезаписан)
285+ msApp.SaveToFile(C_FNAME);
286+
287+ // Обработка ошибки
288+ except
289+ on E:Exception do
290+ writeln('Файл ', C_FNAME, ' не удалось считать или записать, так как: ', E.Message);
291+ end;
292+
293+ // Освобождаем память и уничтожаем объект
294+ msApp.Free;
295+
296+ // Выводим результат операции и ожидаем нажатие Enter
297+ writeln('Файл ', C_FNAME, ' дописан. Нажмите ВВОД для выхода.');
298+ readln;
299+end.
300+```
301+
302+Для работы с файлами большого объёма, рекомендуется использовать буфер, например в 4096 байт.
303+
304+``` pascal
305+var
306+ TotalBytesRead, BytesRead : Int64;
307+ Buffer : array [0..4095] of byte; // или, array [0..4095] of char
308+ FileStream : TFileStream;
309+
310+try
311+ FileStream := TFileStream.Create;
312+ FileStream.Position := 0; // Установим позицию в начало файла
313+ while TotalBytesRead <= FileStream.Size do // Пока объем считанных данных меньше размера файла
314+ begin
315+ BytesRead := FileStream.Read(Buffer,sizeof(Buffer)); // Считать 4096 байт данных
316+ inc(TotalBytesRead, BytesRead); // Увеличиваем TotalByteRead на размер буфера, т.е. 4096 байт
317+ // Что-то делаем с буфером данных
318+ end;
319+```
320+
321+### Копирование файла
322+
323+Теперь,зная методы работы с файлами, мы можем реализовать простую функцию копирования файла, скажем FileCopy.(такой функции нет в FreePascal, хотя Lazarus её имеет [[CopyFile|copyfile]]):
324+
325+``` pascal
326+program FileCopyDemo;
327+// Пример функции FileCopy
328+
329+{$mode objfpc}
330+
331+uses
332+ classes;
333+
334+const
335+ fSource = 'test.txt';
336+ fTarget = 'test.bak';
337+
338+function FileCopy(Source, Target: string): boolean;
339+// Копируем файл с путем в Source в файл с путем Target.
340+// Кэшируем весь файл в память.
341+// В случаи успеха возвращаем true, в случаи ошибки - false.
342+var
343+ MemBuffer: TMemoryStream;
344+begin
345+ result := false;
346+ MemBuffer := TMemoryStream.Create;
347+ try
348+ MemBuffer.LoadFromFile(Source);
349+ MemBuffer.SaveToFile(Target);
350+ result := true
351+ except
352+ //Подавляем исключение; результатом функции является значение false по умолчанию
353+ end;
354+ // Очистка
355+ MemBuffer.Free
356+end;
357+
358+// Пример использования
359+begin
360+ If FileCopy(fSource, fTarget)
361+ then writeln('Файл ', fSource, ' скопирован в ', ftarget)
362+ else writeln('Файл ', fSource, ' не скопирован в ', ftarget);
363+ readln()
364+end.
365+```
366+
367+### Обработка текстовых файлов (TStringList)
368+
369+Для текстовых файлов можно использовать класс TStringList, чтобы загрузить весь файл в память и иметь легкий доступ к его строкам. Вы также можете записать StringList обратно в файл:
370+
371+``` pascal
372+program StringListDemo;
373+{$mode objfpc}
374+
375+uses
376+ Classes, SysUtils;
377+
378+const
379+ C_FNAME = 'textfile.txt';
380+
381+var
382+ slInfo: TStringList;
383+
384+begin
385+ // Создаем TStringList для обработки текстового файла
386+ slInfo := TStringList.Create;
387+
388+ // Для обработки исключений, используем блок try/except
389+ try
390+ // Загружаем файл в память
391+ slInfo.LoadFromFile(C_FNAME);
392+
393+ // Добавляем некие строки
394+ slInfo.Add('Некая строка');
395+ slInfo.Add('Ещё одна строка.');
396+ slInfo.Add('Всё, хватит.');
397+ slInfo.Add('Сейчас ' + DateTimeToStr(now));
398+
399+ // Записать содержимое на диск, заменив исходное содержимое
400+ slInfo.SaveToFile(C_FNAME);
401+
402+ except
403+ // Обработка ошибки
404+ on E: EInOutError do
405+ writeln('Произошла ошибка обработки файла. Причина: ', E.Message);
406+ end;
407+
408+ // Очистка
409+ slInfo.Free;
410+
411+ // Выводим результат операции и ожидаем нажатие Enter
412+ writeln('Файл ', C_FNAME, ' обновлен. Нажмите ВВОД для выхода.');
413+ readln;
414+end.
415+```
416+
417+### Демо: сохранить одну строку в файл
418+
419+Для того, чтобы сохранить одну строку в файл, вы можете воспользоваться процедурой, описанной ниже.
420+
421+``` pascal
422+program SaveStringToPathDemo;
423+{$mode objfpc}
424+
425+uses
426+ Classes, sysutils;
427+
428+const
429+ C_FNAME = 'textstringtofile.txt';
430+
431+// SaveStringToFile: функция для хранения строк текста в файле на диске.
432+// Если результат функции равен True, то строка была написана
433+// Иначе произошла ошибка
434+function SaveStringToFile(theString, filePath: AnsiString): boolean;
435+var
436+ fsOut: TFileStream;
437+begin
438+ // По умолчанию результат неудачный
439+ result := false;
440+
441+ // Записать данную строку в файл, перехватывая ошибки в процессе записи.
442+ try
443+ fsOut := TFileStream.Create(filePath, fmCreate);
444+ fsOut.Write(theString[1], length(theString));
445+ fsOut.Free;
446+
447+ // На данном этапе известно, что запись прошла успешно.
448+ result := true
449+
450+ except
451+ on E:Exception do
452+ writeln('Строка не записана. Детали: ', E.ClassName, ': ', E.Message);
453+ end
454+end;
455+
456+//
457+// Основная программа
458+//
459+begin
460+ // Пытаемся сохранить текст в файл и выводим результат операции
461+ if SaveStringToFile('>> этот текст сохраняется <<', C_FNAME) then
462+ writeln('Текст успешно записан в файл.')
463+ else
464+ writeln('Не удалось сохранить текст в файл.');
465+
466+ // Ждем нажатия Enter
467+ readln
468+end.
469+```
470+
471+## Смотрите так же
472+
473+- [[CopyFile]] - функция Lazarus, которая копирует файл
474+- [[File]]
475+
476+---
477+*From the Free Pascal wiki, page "File Handling In Pascal/ru", as it stood on 2020-02-14 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history