Sign in Join
Wiki copytemporary

File Handling In Pascal: what changed

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

OldNewLine
11## Overview
22
33Something every programmer needs to know is how to work with files. Files are used to persist data i.e. store data in such a way that it can be retrieved at a later time, without having to recreate it. Files can be used to store user settings, error logs, measurement or calculation results, and more. This page explains the basics of file handling.
44
55## Old procedural style
66
77When using files in classic (non-object oriented) Pascal, you can use a file of type TextFile (or simply Text) to store text, that is typically structured in lines. Every line ends with an end-of-line marker ([[End of Line|LineEnding]]). You can store strings in this type of file, but also numbers which can be formatted in different ways. These files can be opened and edited inside the Lazarus IDE or any other text editor.
88
99For specific purposes you can create your own file type that can only store one type of data. For example:
1010
1111``` pascal
1212...
1313type
1414 TIntegerFile = file of integer; // Allows you to write only integer numbers to the file
1515 TExtendedFile = file of extended; // Allows you to write only real numbers to the file
1616 TCharFile = file of char; // Allows you to write only single characters to the file
1717```
1818
1919### Input/Output error handling
2020
2121The [I/O error handling flag](http://www.freepascal.org/docs-html/prog/progsu38.html#x45-440001.2.38) tells the compiler how to deal with error situations: raise an exception or store the I/O result in the IOResult variable. The I/O error handling flag is a compiler directive. To enable or disable it:
2222
2323``` pascal
2424{$I+} // Errors will lead to an EInOutError exception (default)
2525{$I-} // Suppress I/O errors: check the IOResult variable for the error code
2626```
2727
2828By suppressing I/O errors ({\$I-}) the file operation results go into the IOResult variable. This is a [[Cardinal|cardinal (number) type]]. Different numbers mean different errors. So you may want to check the documentation for the different errors[1](http://www.freepascal.org/docs-html/rtl/system/ioresult.html).
2929
3030### File procedures
3131
3232These file handling procedures and functions are located in [[System unit|unit System]]. See the FPC documentation for more details: [Reference for 'System' unit](http://www.freepascal.org/docs-html/rtl/system/index-5.html).
3333
3434- **AssignFile** (prevent the use of the older **[[Assign]]** procedure) - Assign a name to a file
3535- **Append** - Opens an existing file for appending data to end of file and editing it
3636- **BlockRead** - Read data from an untyped file into memory
3737- **BlockWrite** - Write data from memory to an untyped file
3838- **CloseFile** (prevent the use of the older **Close** procedure) - Close opened file
3939- **EOF** - Check for end of file
4040- **Erase** - Erase file from disk
4141- **FilePos** - Get position in file
4242- **FileSize** - Get size of file
4343- **Flush** - Write file buffers to disk
4444- **IOResult** - Return result of last file IO operation
4545- **Read** - Read from a text file
4646- **ReadLn** - Read from a text file and go to the next line
4747- **Reset** - Opens a file for reading
4848- **Rewrite** - Create a file for writing
4949- **Seek** - Change position in file
5050- **SeekEOF** - Set file position to end of file
5151- **SeekEOLn** - Set file position to end of line
5252- **Truncate** - Truncate the file at position
5353- **[[Write]]** - Write variable to a file
5454- **WriteLn** - Write variable to a text file and go to a new line
5555
5656### Example
5757
5858A full example of handling a text file of type TextFile:
5959
6060``` pascal
6161program CreateFile;
6262
6363{$mode objfpc}
6464
6565uses
6666 Sysutils;
6767
6868const
6969 C_FNAME = 'textfile.txt';
7070
7171var
7272 tfOut: TextFile;
7373
7474begin
7575 // Set the name of the file that will be created
7676 AssignFile(tfOut, C_FNAME);
7777
7878 // Use exceptions to catch errors (this is the default so not absolutely requried)
7979 {$I+}
8080
8181 // Embed the file creation in a try/except block to handle errors gracefully
8282 try
8383 // Create the file, write some text and close it.
8484 rewrite(tfOut);
8585
8686 writeln(tfOut, 'Hello textfile!');
8787 writeln(tfOut, 'The answer to life, the universe and everything: ', 42);
8888
8989 CloseFile(tfOut);
9090
9191 except
9292 // If there was an error the reason can be found here
9393 on E: EInOutError do
9494 writeln('File handling error occurred. Details: ', E.ClassName, '/', E.Message);
9595 end;
9696
9797 // Give feedback and wait for key press
9898 writeln('File ', C_FNAME, ' created if all went ok. Press Enter to stop.');
9999 readln;
100100end.
101101```
102102
103103Now open the file in any text editor and you will see the above text written to it! You can test the error handling by running the program once, then set the file to read-only and run the program again.
104104
105105Note that exception handling was used as that is an easy way to perfom multiple file operations and handling the errors. You could also use {\$I-}, but then you would have to check IOResult after each operation and modify your next operation.
106106
107107Here's how appending more text to a textfile works:
108108
109109``` pascal
110110program AppendToFile;
111111
112112{$mode objfpc}
113113
114114uses
115115 Sysutils;
116116
117117const
118118 C_FNAME = 'textfile.txt';
119119
120120var
121121 tfOut: TextFile;
122122
123123begin
124124 // Set the name of the file that will receive some more text
125125 AssignFile(tfOut, C_FNAME);
126126
127127 // Embed the file handling in a try/except block to handle errors gracefully
128128 try
129129 // Open the file for appending, write some more text to it and close it.
130130 append(tfOut);
131131
132132 writeln(tfOut, 'Hello again textfile!');
133133 writeln(tfOut, 'The result of 6 * 7 = ', 6 * 7);
134134
135135 CloseFile(tfOut);
136136
137137 except
138138 on E: EInOutError do
139139 writeln('File handling error occurred. Details: ', E.Message);
140140 end;
141141
142142 // Give feedback and wait for key press
143143 writeln('File ', C_FNAME, ' might have more text. Press enter to stop.');
144144 readln;
145145end.
146146```
147147
148148Reading a textfile:
149149
150150``` pascal
151151program ReadFile;
152152
153153{$mode objfpc}
154154
155155uses
156156 Sysutils;
157157
158158const
159159 C_FNAME = 'textfile.txt';
160160
161161var
162162 tfIn: TextFile;
163163 s: string;
164164
165165begin
166166 // Give some feedback
167167 writeln('Reading the contents of file: ', C_FNAME);
168168 writeln('=========================================');
169169
170170 // Set the name of the file that will be read
171171 AssignFile(tfIn, C_FNAME);
172172
173173 // Embed the file handling in a try/except block to handle errors gracefully
174174 try
175175 // Open the file for reading
176176 reset(tfIn);
177177
178178 // Keep reading lines until the end of the file is reached
179179 while not eof(tfIn) do
180180 begin
181181 readln(tfIn, s);
182182 writeln(s);
183183 end;
184184
185185 // Done so close the file
186186 CloseFile(tfIn);
187187
188188 except
189189 on E: EInOutError do
190190 writeln('File handling error occurred. Details: ', E.Message);
191191 end;
192192
193193 // Wait for the user to end the program
194194 writeln('=========================================');
195195 writeln('File ', C_FNAME, ' was probably read. Press enter to stop.');
196196 readln;
197197end.
198198```
199199
200200## Object style
201201
202202In addition to the old style file handling routines mentioned above, a new system exists that uses the concept of streams (- of data) at a higher abstraction level. This means data can be read from or written to any location (disk, memory, hardware ports etc.) by one uniform interface.
203203
204204In addition, most string handling classes have the ability to load and save content from/to a file. These methods are usually named SaveToFile and LoadFromFile. A lot of other objects (such as Lazarus grids) have similar functionality, including Lazarus datasets (DBExport). It pays to look through the documentation or source code before trying to roll your own save/load routines.
205205
206206### Generic files of any type
207207
208208For opening files for direct access TFileStream can be used. This class is an encapsulation of the system procedures FileOpen, FileCreate, FileRead, FileWrite, FileSeek and FileClose which resides in unit SysUtils.
209209
210210[IO routines](http://www.freepascal.org/docs-html/rtl/sysutils/ioroutines.html)
211211
212212Here is a very simple example of appending one file to another using streams. It is equivalent to the append example above, but much simpler:
213213
214214``` pascal
215215program InOutStream;
216216
217217{$mode objfpc}
218218
219219uses
220220 Classes, SysUtils;
221221var
222222 InStream, OutStream: TFileStream;
223223begin
224224 if (ParamCount = 2) and
225225 FileExists(ParamStr(1)) and
226226 FileExists(ParamStr(2)) then
227227 begin
228228 InStream := TFileStream.Create(ParamStr(1), fmOpenRead);
229229 try
230230 OutStream := TFileStream.Create(Paramstr(2), fmOpenWrite);
231231 try
232232 OutStream.Position := OutStream.size;
233233 OutStream.CopyFrom(InStream, 0); // appends
234234 finally
235235 OutStream.Free;
236236 end;
237237 finally
238238 InStream.Free;
239239 end;
240240 end else
241241 Writeln('Usage: inoutstream <infile> <outfile>');
242242end.
243243```
244244
245-\
246245In the example below, note how we encapsulate the file handling action with a try..except block so that errors are handled correctly just as with the classic file handling routines (as not to convolute the example the fsOut.write is not put inside a try...finally block).
247246
248247``` pascal
249248program WriteBinaryData;
250249
251250{$mode objfpc}
252251
253252uses
254253 Classes, Sysutils;
255254
256255const
257256 C_FNAME = 'binarydata.bin';
258257
259258var
260259 fsOut : TFileStream;
261260 ChrBuffer: array[0..2] of char;
262261
263262begin
264263 // Set up some random data that will get stored
265264 ChrBuffer[0] := 'A';
266265 ChrBuffer[1] := 'B';
267266 ChrBuffer[2] := 'C';
268267
269268 // Catch errors in case the file cannot be created
270269 try
271270 // Create the file stream instance, write to it and free it to prevent memory leaks
272271 fsOut := TFileStream.Create( C_FNAME, fmCreate);
273272 fsOut.Write(ChrBuffer, sizeof(ChrBuffer));
274273 fsOut.Free;
275274
276275 // Handle errors
277276 except
278277 on E:Exception do
279278 writeln('File ', C_FNAME, ' could not be created because: ', E.Message);
280279 end;
281280
282281 // Give feedback and wait for key press
283282 writeln('File ', C_FNAME, ' created if all went ok. Press Enter to stop.');
284283 readln;
285284end.
286285```
287286
288287You can load entire files into memory too if its size is comparatively smaller than available system memory. Bigger sizes might work but your operating system will start using the page/swap file, making the exercise useless from a performance standpoint.
289288
290289``` pascal
291290program ReadBinaryDataInMemoryForAppend;
292291
293292{$mode objfpc}
294293
295294uses
296295 Classes, Sysutils;
297296
298297const
299298 C_FNAME = 'binarydata.bin';
300299
301300var
302301 msApp: TMemoryStream;
303302
304303begin
305304 // Set up the stream
306305 msApp := TMemoryStream.Create;
307306
308307 // Catch errors in case the file cannot be read or written
309308 try
310309 // Read the data into memory
311310 msApp.LoadFromFile(C_FNAME);
312311
313312 // Seek the end of the stream so data can be appended
314313 msApp.Seek(0, soEnd);
315314
316315 // Write some arbitrary data to the memory stream
317316 msApp.WriteByte(68);
318317 msApp.WriteAnsiString('Some extra text');
319318 msApp.WriteDWord(671202);
320319
321320 // Store the data back on disk, overwriting the previous contents
322321 msApp.SaveToFile(C_FNAME);
323322
324323 // Handle errors
325324 except
326325 on E:Exception do
327326 writeln('File ', C_FNAME, ' could not be read or written because: ', E.Message);
328327 end;
329328
330329 // Clean up
331330 msApp.Free;
332331
333332 // Give feedback and wait for key press
334333 writeln('File ', C_FNAME, ' was extended if all went ok. Press Enter to stop.');
335334 readln;
336335end.
337336```
338337
339338With larger files of many Gb, you may want to read in buffers of, say, 4096 bytes (you're advised to use a multiple of the filesytem cluster or block size) and do something with the data of each buffer read.
340339
341340``` pascal
342341var
343342 TotalBytesRead, BytesRead : Int64;
344343 Buffer : array [0..4095] of byte; // or, array [0..4095] of char
345344 FileStream : TFileStream;
346345
347346try
348347 FileStream := TFileStream.Create;
349348 FileStream.Position := 0; // Ensure you are at the start of the file
350349 TotalBytesRead := 0;
351350 while TotalBytesRead <= FileStream.Size do // While the amount of data read is less than or equal to the size of the stream do
352351 begin
353352 BytesRead := FileStream.Read(Buffer, SizeOf(Buffer)); // Read in 4096 of data
354353 Inc(TotalBytesRead, BytesRead); // Increase TotalByteRead by the size of the buffer, i.e. 4096 bytes
355354 // Do something with Buffer data
356355 end;
357356```
358357
359358### FileCopy
360359
361360With the above, we can implement a simple FileCopy function (FreePascal has none in its RTL although Lazarus has [[CopyFile]]) - adjust as needed for bigger files etc:
362361
363362``` pascal
364363program FileCopyDemo;
365364// Demonstration of FileCopy function
366365
367366{$mode objfpc}
368367
369368uses
370369 Classes;
371370
372371const
373372 fSource = 'test.txt';
374373 fTarget = 'test.bak';
375374
376375function FileCopy(Source, Target: string): boolean;
377376// Copies source to target; overwrites target.
378377// Caches entire file content in memory.
379378// Returns true if succeeded; false if failed.
380379var
381380 MemBuffer: TMemoryStream;
382381begin
383382 Result := false;
384383 MemBuffer := TMemoryStream.Create;
385384 try
386385 MemBuffer.LoadFromFile(Source);
387386 MemBuffer.SaveToFile(Target);
388387 Result := true
389388 except
390389 //swallow exception; function result is false by default
391390 end;
392391 // Clean up
393392 MemBuffer.Free
394393end;
395394
396395begin
397396 If FileCopy(fSource, fTarget)
398397 then Writeln('File ', fSource, ' copied to ', fTarget)
399398 else Writeln('File ', fSource, ' not copied to ', fTarget);
400399 Readln()
401400end.
402401```
403402
404403### Handling Text files (TStringList)
405404
406405In general, for text files you can use the TStringList class to load the entire file into memory and have easy access to its lines. Of course, you can also write the StringList back to a file:
407406
408407``` pascal
409408program StringListDemo;
410409
411410{$mode objfpc}
412411
413412uses
414413 Classes, SysUtils;
415414
416415const
417416 C_FNAME = 'textfile.txt';
418417
419418var
420419 slInfo: TStringList;
421420
422421begin
423422 // Create an instance of the string list to handle the textfile
424423 slInfo := TStringList.Create;
425424
426425 // Embed the file handling in a try/except block to handle errors gracefully
427426 try
428427 // Load the contents of the textfile completely in memory
429428 slInfo.LoadFromFile(C_FNAME);
430429
431430 // Add some more contents
432431 slInfo.Add('An extra line appended to the text');
433432 slInfo.Add('And another one.');
434433 slInfo.Add('Let''s stop here.');
435434 slInfo.Add('It is now ' + DateTimeToStr(now));
436435
437436 // And write the contents back to disk, replacing the original contents
438437 slInfo.SaveToFile(C_FNAME);
439438
440439 except
441440 // If there was an error the reason can be found here
442441 on E: EInOutError do
443442 writeln('File handling error occurred. Reason: ', E.Message);
444443 end;
445444
446445 // Clean up
447446 slInfo.Free;
448447
449448 // Give feedback and wait for key press
450449 writeln('File ', C_FNAME, ' updated if all went ok. Press Enter to stop.');
451450 readln;
452451end.
453452```
454453
455454### Demo: save single string to a file
456455
457456In order to write a single string to a stream you might want to use the procedure defined below. Note that strings in FreePascal can be extremely long, this is also a useful way to write a big block of textdata to a file.
458457
459458``` pascal
460459program SaveStringToPathDemo;
461460
462461{$mode objfpc}
463462
464463uses
465464 Classes, sysutils;
466465
467466const
468467 C_FNAME = 'textstringtofile.txt';
469468
470469// SaveStringToFile: function to store a string of text into a diskfile.
471470// If the function result equals true, the string was written ok.
472471// If not then there was some kind of error.
473472function SaveStringToFile(theString, filePath: AnsiString): boolean;
474473var
475474 fsOut: TFileStream;
476475begin
477476 // By default assume the writing will fail.
478477 result := false;
479478
480479 // Write the given string to a file, catching potential errors in the process.
481480 try
482481 fsOut := TFileStream.Create(filePath, fmCreate);
483482 fsOut.Write(theString[1], length(theString));
484483 fsOut.Free;
485484
486485 // At his point it is known that the writing went ok.
487486 result := true
488487
489488 except
490489 on E:Exception do
491490 writeln('String could not be written. Details: ', E.ClassName, ': ', E.Message);
492491 end
493492end;
494493
495494//
496495// Main program
497496//
498497begin
499498 // Try to save a simple textstring in a file and give feedback of sucess.
500499 if SaveStringToFile('>> this text gets stored <<', C_FNAME) then
501500 writeln('Text succesfully written to a file')
502501 else
503502 writeln('Writing text to a file failed.');
504503
505504 // Wait for the user to press Enter
506505 readln
507506end.
508507```
509508
510509## File Handling in ISO Mode
511510
512511### Command Line Parameters
513512
514513The first option to assign external to internal files in iso mode is through command line parameters according to their order of appearance. Example:
515514
516515``` pascal
517516program isotest1 (file1, file2);
518517var
519518 file1: text;
520519 file2: file of integer;
521520begin
522521 reset(file1);
523522 readln(file1);
524523 rewrite(file2);
525524 write(file2, 5);
526525end.
527526```
528527
529528The program is compiled with `fpc -Miso isotest1.pas` and run with `./isotest1 external1.txt external2.dat`. files1 is assigned to external1.txt and file2 to external2.dat. Without command line parameters the files are assigned to stdin and stdout. As long as there is only one file for input and one for output, the same assignment can be achieved with this command: `./isotest1 <external1.txt >external2.dat`. file1 must exist prior to the run, file2 will be created.
530529
531530### Transparent File Names
532531
533532The second option is transparent file names, achieved with the compiler option -Sr. Example:
534533
535534``` pascal
536535program isotest2 (file1, file2);
537536var
538537 file1: text;
539538 file2: text;
540539begin
541540 reset(file1);
542541 readln(file1);
543542 rewrite(file2);
544543 write(file2, 5);
545544end.
546545```
547546
548547If the program is compiled with `fpc -Miso -Sr isotest2.pas` and run without command line parameters, the external files FILE1.txt and FILE2.txt are read and written. Note the uppercase conversion of the internal filenames. Files with different filenames can still be used through command line parameters.
549548
550549## See also
551550
552551- [[CopyFile]] Lazarus function (also available for command line programs) that copies files for you
553552- [[File]]
554553- [[Howto Use TOpenDialog]]
555554- [[Howto Use TSaveDialog]]
556555
557556---
558557*From the Free Pascal wiki, page "File Handling In Pascal", as it stood on 2023-10-12 after a change by FPC user, 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