I/O Operations in Pascal
Learn console input and output, formatted printing, and reading and writing text files in Pascal with Docker-ready Free Pascal examples
Input and output are where a program meets the outside world. Pascal, as an imperative and structured language, treats I/O through a small, consistent vocabulary of built-in procedures: Write and WriteLn send data out, while Read and ReadLn bring data in. The same four procedures work for the console and for files — the only difference is an optional first argument naming a file variable.
This design reflects Pascal’s teaching heritage. Rather than a sprawling I/O library, Wirth gave the language a handful of orthogonal primitives that compose predictably. A TextFile variable behaves like the console; the console behaves like a text file. Once you understand WriteLn printing to the screen, writing to disk is the same call with one extra parameter.
In this tutorial you’ll go beyond the WriteLn('Hello, World!') from the first lesson. You’ll format numbers with field-width specifiers, read typed input from the keyboard, write and read text files, and handle I/O errors the Pascal way — with compiler directives and the IOResult function. All examples run under Free Pascal (FPC).
Formatted Console Output
Pascal’s Write and WriteLn accept a colon-based formatting syntax. For any value you can append :width to set a minimum field width (right-aligned), and for real numbers a second :decimals controls fixed-point precision. This avoids the format-string mini-languages of other languages — the formatting travels with each argument.
Create a file named formatted_output.pas:
program FormattedOutput;
var
count: Integer;
price: Real;
itemName: string;
begin
count := 42;
price := 19.99;
itemName := 'Widget';
{ Plain output - values are converted automatically }
WriteLn('Product: ', itemName);
WriteLn('Count: ', count);
{ :width right-aligns an integer in a field of that width }
WriteLn('Count padded:', count:6);
{ :width:decimals gives fixed-point formatting for reals }
WriteLn('Price: ', price:0:2);
WriteLn('Price padded: ', price:10:2);
{ Booleans print as TRUE or FALSE }
WriteLn('In stock: ', count > 0);
end.
Without :0:2, a Real would print in scientific notation (for example 1.999000000000E+01), so the width/decimals specifier is the idiomatic way to show currency-style numbers.
Reading Input from the Console
ReadLn reads a whole line from standard input and converts it to the type of the target variable. Reading into a string captures the text; reading into an Integer or Real parses the number. Because ReadLn consumes the trailing newline, consecutive calls line up cleanly with the user pressing Enter after each entry.
Create a file named console_input.pas:
program ConsoleInput;
var
userName: string;
age: Integer;
begin
Write('Enter your name: ');
ReadLn(userName);
Write('Enter your age: ');
ReadLn(age);
WriteLn('Hello, ', userName, '!');
WriteLn('Next year you will be ', age + 1, ' years old.');
end.
Note the use of Write (not WriteLn) for the prompts, so the cursor stays on the same line as the user’s typing. With the input Ada followed by 36, this program produces:
Enter your name: Ada
Enter your age: 36
Hello, Ada!
Next year you will be 37 years old.
Writing and Reading Text Files
File I/O reuses the same Write/ReadLn procedures, adding a TextFile variable as the first argument. The lifecycle is always the same four steps: AssignFile links a variable to a path, Rewrite opens it for writing (creating or truncating), Reset opens it for reading, and CloseFile flushes and releases it. The Eof function tells you when a file being read is exhausted.
Create a file named file_io.pas:
program FileIO;
var
outFile, inFile: TextFile;
line: string;
lineNum: Integer;
begin
{ Open notes.txt for writing (creates or overwrites) }
AssignFile(outFile, 'notes.txt');
Rewrite(outFile);
WriteLn(outFile, 'First line');
WriteLn(outFile, 'Second line');
WriteLn(outFile, 'Third line');
CloseFile(outFile);
WriteLn('Wrote 3 lines to notes.txt');
{ Reopen the same file for reading }
AssignFile(inFile, 'notes.txt');
Reset(inFile);
lineNum := 0;
while not Eof(inFile) do
begin
ReadLn(inFile, line);
Inc(lineNum);
WriteLn('Line ', lineNum, ': ', line);
end;
CloseFile(inFile);
end.
The while not Eof(...) loop is the canonical Pascal pattern for reading a text file line by line — it works regardless of how many lines the file contains.
Handling I/O Errors
By default, Free Pascal raises a runtime error and aborts if an I/O operation fails — for example, opening a file that doesn’t exist. To handle failures gracefully, wrap the risky call between the {$I-} and {$I+} compiler directives to switch off automatic checking, then inspect the IOResult function. IOResult returns 0 on success or an error code otherwise, and reading it clears the pending error.
Create a file named file_errors.pas:
program FileErrors;
var
inFile: TextFile;
line: string;
begin
AssignFile(inFile, 'missing.txt');
{$I-} { turn off automatic I/O error checking }
Reset(inFile);
{$I+} { turn it back on for the rest of the program }
if IOResult <> 0 then
begin
WriteLn('Error: could not open missing.txt');
Halt(1);
end;
ReadLn(inFile, line);
WriteLn('First line: ', line);
CloseFile(inFile);
end.
Because missing.txt does not exist, Reset fails, IOResult returns a non-zero code, and the program reports the error and exits with status 1 via Halt instead of crashing.
Running with Docker
You can compile and run any of these examples with the official Free Pascal image — no local install required.
| |
To run a different example, swap the program name in both the compile and run steps, for example fpc formatted_output.pas && ./formatted_output.
Expected Output
Running the file_io.pas example produces:
Wrote 3 lines to notes.txt
Line 1: First line
Line 2: Second line
Line 3: Third line
The formatted_output.pas example produces:
Product: Widget
Count: 42
Count padded: 42
Price: 19.99
Price padded: 19.99
In stock: TRUE
And file_errors.pas (with no missing.txt present) produces:
Error: could not open missing.txt
Key Concepts
- One vocabulary for all I/O —
Write,WriteLn,Read, andReadLnhandle both the console and files; aTextFilefirst argument is the only difference. - Colon formatting —
value:widthright-aligns in a field, andreal:width:decimalsgives fixed-point precision. Always use:decimalsfor reals to avoid scientific notation. - File lifecycle —
AssignFile→Rewrite(write) orReset(read) →Write/ReadLn→CloseFile. ForgettingCloseFilecan leave buffered data unwritten. Rewriteis destructive — it creates a new file or truncates an existing one. To add to a file without erasing it, useAppendinstead.Eofdrives read loops —while not Eof(f) do ReadLn(f, line)is the standard idiom for consuming a text file of unknown length.- Errors are opt-in to handle — FPC aborts on I/O failure by default; bracket a call with
{$I-}/{$I+}and checkIOResult(which also clears the error) to recover gracefully. WritevsWriteLnmatters for prompts — useWriteso the input cursor stays on the same line as the prompt text.
Running Today
All examples can be run using Docker:
docker pull freepascal/fpc:latest
Comments
Loading comments...
Leave a Comment