I/O Operations in Delphi
Learn console and file input/output in Delphi with WriteLn, ReadLn, TextFile handling, formatted output, and I/O error handling using Docker-ready examples
Input and output are how a program talks to the outside world — reading what a user types, printing results to the terminal, and persisting data to disk. Delphi inherits Pascal’s clean, readable I/O routines while layering on SysUtils conveniences like the Format function and structured exception handling for I/O errors.
As a multi-paradigm language with strong static typing, Delphi’s I/O reflects its heritage: the core Write, WriteLn, Read, and ReadLn procedures come straight from Pascal, while file access uses typed file handles (TextFile) that you associate, open, use, and close explicitly. Because Delphi is strongly typed, ReadLn can read directly into an Integer or Double variable and perform the conversion for you.
In this tutorial you’ll learn how to produce formatted console output, read input from the keyboard, write and read text files, and handle the errors that inevitably occur when working with the file system. Every example runs under Free Pascal’s Delphi compatibility mode (-Mdelphi), so no commercial Delphi installation is required.
Console Output
You already met WriteLn in Hello World. The full picture: Write outputs text without a trailing newline, while WriteLn appends one. Combined with SysUtils.Format — Delphi’s printf-style formatter — you get precise control over how values appear.
Create a file named console_output.dpr:
program ConsoleOutput;
{$APPTYPE CONSOLE}
uses
SysUtils; // Provides the Format function
var
Quantity: Integer;
Price: Double;
begin
// Write does NOT add a newline; WriteLn does
Write('Loading');
Write('...');
WriteLn(' done!');
Quantity := 3;
Price := 4.99;
// Format provides printf-style substitution
WriteLn(Format('Items: %d', [Quantity]));
WriteLn(Format('Price: $%.2f', [Price]));
WriteLn(Format('Total: $%.2f', [Quantity * Price]));
// Field width and alignment: %-10s left-justifies, %5d right-justifies
WriteLn(Format('%-10s|%5d', ['Left', 42]));
end.
Key format specifiers:
%d— integer%.2f— floating-point with 2 decimal places%s— string%-10s— string left-justified in a 10-character field%5d— integer right-justified in a 5-character field
Reading Console Input
To read from the keyboard, use ReadLn. Because Delphi is strongly typed, reading into a string captures the whole line, while reading into a numeric variable performs the conversion automatically.
Create a file named console_input.dpr:
program ConsoleInput;
{$APPTYPE CONSOLE}
uses
SysUtils;
var
Name: string;
Age: Integer;
begin
Write('Enter your name: ');
ReadLn(Name); // Reads an entire line into a string
Write('Enter your age: ');
ReadLn(Age); // Reads and converts directly to Integer
WriteLn(Format('Hello, %s!', [Name]));
WriteLn(Format('Next year you will be %d.', [Age + 1]));
end.
This program is interactive. A sample session where the user types Ada and 36:
Enter your name: Ada
Enter your age: 36
Hello, Ada!
Next year you will be 37.
You can feed input non-interactively by piping it into the program, which is handy for testing:
| |
Writing to Files
Delphi’s classic text file I/O follows a four-step pattern: associate a variable with a filename (AssignFile), open it (Rewrite to create/overwrite, or Append to add to the end), use it with Write/WriteLn, and close it (CloseFile). A try..finally block guarantees the file is closed even if an error occurs.
Create a file named file_write.dpr:
program FileWrite;
{$APPTYPE CONSOLE}
uses
SysUtils;
var
OutFile: TextFile;
I: Integer;
begin
AssignFile(OutFile, 'notes.txt'); // Associate variable with filename
Rewrite(OutFile); // Create (or overwrite) the file
try
WriteLn(OutFile, 'Delphi I/O Notes');
WriteLn(OutFile, '================');
for I := 1 to 3 do
WriteLn(OutFile, Format('Line %d', [I]));
finally
CloseFile(OutFile); // Always close, even on error
end;
WriteLn('Wrote notes.txt');
end.
Notice that WriteLn(OutFile, ...) is the same procedure used for the console — passing a TextFile as the first argument simply redirects the output to that file.
Reading from Files
Reading mirrors writing: associate, open with Reset, read line by line until Eof (end of file) returns True, then close. Guard against a missing file first with FileExists.
Create a file named file_read.dpr:
program FileRead;
{$APPTYPE CONSOLE}
uses
SysUtils;
var
InFile: TextFile;
Line: string;
Count: Integer;
begin
if not FileExists('notes.txt') then
begin
WriteLn('notes.txt not found. Run file_write first.');
Halt(1);
end;
AssignFile(InFile, 'notes.txt');
Reset(InFile); // Open the file for reading
Count := 0;
try
while not Eof(InFile) do
begin
ReadLn(InFile, Line); // Read one line at a time
Inc(Count);
WriteLn(Format('%d: %s', [Count, Line]));
end;
finally
CloseFile(InFile);
end;
WriteLn(Format('Read %d lines.', [Count]));
end.
Handling I/O Errors
By default, Delphi mode compiles with I/O checking on ({$I+}), so a failed file operation raises an EInOutError exception. Wrapping risky operations in try..except lets your program recover gracefully instead of crashing.
Create a file named io_operations.dpr:
program IoOperations;
{$APPTYPE CONSOLE}
uses
SysUtils;
var
DataFile: TextFile;
Line: string;
begin
// Attempt to open a file that does not exist
AssignFile(DataFile, 'missing.txt');
try
Reset(DataFile); // Raises EInOutError because the file is missing
try
ReadLn(DataFile, Line);
WriteLn('Read: ', Line);
finally
CloseFile(DataFile);
end;
except
on E: EInOutError do
WriteLn('I/O error: could not open missing.txt');
end;
WriteLn('Program continues after handling the error.');
end.
The on E: EInOutError do clause catches only I/O-related exceptions, letting you report a friendly message while the program keeps running. This structured approach is one of the biggest advantages Delphi/Object Pascal has over standard Pascal, which had no exception mechanism at all.
Running with Docker
Compile and run each example in sequence using the Free Pascal image. Because file_read.dpr depends on the file created by file_write.dpr, run the write step first.
| |
The executable is named after the source file (without the .dpr extension), so console_output.dpr compiles to ./console_output.
Expected Output
Running console_output:
Loading... done!
Items: 3
Price: $4.99
Total: $14.97
Left | 42
Running file_write followed by file_read:
Wrote notes.txt
1: Delphi I/O Notes
2: ================
3: Line 1
4: Line 2
5: Line 3
Read 5 lines.
Running io_operations:
I/O error: could not open missing.txt
Program continues after handling the error.
Key Concepts
WritevsWriteLn—Writeprints without a trailing newline;WriteLnadds one. The same procedures write to both the console and files.Formatfor formatted output —SysUtils.Formatgivesprintf-style control with specifiers like%d,%.2f,%s, and field widths such as%-10sand%5d.- Strong typing helps input —
ReadLnreads directly into typed variables, converting text toIntegerorDoubleautomatically instead of requiring a manual parse. - The file lifecycle — text file I/O follows
AssignFile→ open (Rewrite/Append/Reset) → use →CloseFile. Redirect anyWrite/WriteLnto a file by passing theTextFilevariable as the first argument. - Always close in
finally— atry..finallyblock guaranteesCloseFileruns even if an exception is raised mid-operation. - Check before you read —
FileExistsand theEoffunction help you avoid errors before and during reading. - Structured I/O error handling — with I/O checking on (the Delphi-mode default), failed operations raise
EInOutError, which you can catch withtry..except on E: EInOutErrorto recover gracefully.
Running Today
All examples can be run using Docker:
docker pull freepascal/fpc:3.2.2-slim
Comments
Loading comments...
Leave a Comment