Second item:
Numbers[1]:= 315;
Last item:
Numbers[9]:= 10;
or
Numbers[High(Numbers)]:= 10;
Records
While arrays can hold many variables of the same type, records can hold variables of different types, and these variables are called ' Fields'.
This group of variables/fields can be treated as a single unit or variable. We can use records to store information that belong to the same object, for example, car information:
1. Car type: string variable
2. Engine size: real number
3. Production year: integer value
We can collect these different types in one record which represents a Car as in the following example: program Cars;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Classes
{ you can add units after this };
type
TCar = record
ModelName: string;
Engine: Single;
ModelYear: Integer;
end;
var
Car: TCar;
begin
Write('Input car Model Name: ');
Readln(Car.ModelName);
Write('Input car Engine size: ');
Readln(Car.Engine);
Write('Input car Model year: ');
Readln(Car.ModelYear);
Writeln;
Writeln('Car information: ');
Writeln('Model Name : ', Car.ModelName);
Writeln('Engine size : ', Car.Engine);
Writeln('Model Year : ', Car.ModelYear);
Write('Press enter key to close..');
Readln;
end.
In this example ,we have defined a new type ( Record) using the ' type' keyword: type
TCar = record
ModelName: string;
Engine: Single;
ModelYear: Integer;
end;
We have added the letter ( T) to Car to indicate that this is a type and not a variable. Variable names could be like: Car, Hour, UserName, but type names should be like: TCar, THouse, and TUserName.
This is a standard in the Pascal language.
When we need to use this new type, then we should declare a variable of that type, for example: var
Car: TCar;
If we need to store a value in one of its variables/fields, we should access it like this:
Car.ModelName
Records will be used in the Random access files section of this book.
Files
Files are important elements of operating systems and applications. Operating system components are represented as files, and information and data are represented in files too, like photos, books, applications, and simple text files.
Operating systems control files management like: reading, writing, editing and deleting files.
Files are divided into many types according to many perspectives. We can group files into two types: executable files, and data files. For example compiled binary Lazarus applications are executable files, while Pascal source code (.pas) are data files. Also PDF books, JPEG pictures, are data files.
We can divide data files into two types according to the representation of their contents: 1. Text files: which are simple text files that can be written, or read using any simple tool including operating system command lines like cat, vi commands in Linux and type, copy con commands in Windows.
2. Binary data files: These are more complex and need special applications to open them. For example, picture files can not be opened using simple command line tools, instead they should be opened using applications like GIMP, Kolour Paint, MS Paint, etc. If we open picture, or voice files, we will get unrecognizable characters which mean nothing to the user. Examples of binary data files are database files, which should be opened using the proper application.
There is another way to categorize files according to access type:
1. Sequential access files: An example of a sequential file is a text file, which has no fixed size record. Each line has its own length, so we can't know the position (in characters) for the start of the third line for example. For that reason, we could open the file for read only, or writing only, and we can also append text only to the end of file. If we need to insert text in the middle of a file, then we should read file contents into memory, do the modifications, erase the entire file from disk, then overwrite it the file with the modified text.
2. Random access files: This type of file has a fixed size record. A record is the smallest unit that we can read and write at one time, and it could be Byte, Integer, string, or a user defined record.
We can read and write at the same time, for example, we could read the record number 3 and copy it to the record number 10. In this case, we can know exactly the position of each record in the file. Modifying records is simple. In random access files, we can replace/overwrite any record without affecting the rest of the file.
Text files
Text files are the simplest files, but we should write to them in only one direction (only forward). We can not go back while writing to a text file. We should also define the operation mode before opening the file: Read, Write or Append (writing at the end).
In this example, we will display the contents of a text file selected by a user. For example, a user may have a text file name like c:\test\first.pas in Windows or /home/user/first.pas in Linux: Reading text file program
program ReadFile;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Classes, sysUtils
{ you can add units after this };
var
FileName: string;
F: TextFile;
Line: string;
begin
Write('Input a text file name: ');
Readln(FileName);
if FileExists(FileName) then
begin
// Link file variable (F) with physical file (FileName)
AssignFile(F, FileName);
Reset(F); // Put file mode for read, file should exist
// while file has more lines that does not read yet do the loop
while not Eof(F) do
begin
Readln(F, Line); // Read a line from text file
Writeln(Line); // Display this line in user screen
end;
CloseFile(F); // Release F and FileName connection
end
else // else if FileExists..
Writeln('File does not exist');
Write('Press enter key to close..');
Readln;
end.
In Linux we can enter these file names:
/etc/resolv.conf
or
/proc/meminfo
/proc/cpuinfo
We have used new types, functions and procedures to manipulate text files which are: 1.
F: TextFile;
TextFile is a type that is used to declare a text file variable. This variable can be linked to a text file for later usage.
2.
if FileExists(FileName) then
FileExists is a function contained in the SysUtils unit. It checks for a file's existence. It returns True if the file exists in the storage media.
3.
AssignFile(F, FileName);
After we have checked for the file's existence, we can use the AssignFile procedure to link the file variable ( F) with the physical file. Any further usage of F variable will represent the physical file.
4.
Reset(F); // Put file mode for read, file should exist
The Reset procedure opens text files for reading only, and puts the reading pointer at the first character of the file.
If there is no read permission for that file for the current user, an error message will appear, like access denied.
5.
Readln(F, Line); // Read a line from text file
The procedure Readln is used to read a complete line from the file and put it in the variable Line.
Before reading a line from text file, we should make sure that the file has not reached the end. We can do that with the next function, Eof.
6.
while not Eof(F) do
The Eof function returns True if the file has reached its end. It is used to indicate that the Read operation couldn't be used anymore and we have read all of the file's contents.
7.
CloseFile(F); // Release F and FileName connection
After finishing reading from or writing to a file, we should close it to release the file, because the Reset procedure reserves the file in the operating system and prevents writing and deletion by another application while the file is open.
We should use CloseFile only when the Reset procedure succeeds in opening the file. If Reset fails (for example, if the file does not exist, or is being used by another application) in that case we should not close the file.
In the next example we will create a new text file and put some text in it:
Creating and writing into text file
var
FileName: string;
F: TextFile;
Line: string;
ReadyToCreate: Boolean;
Ans: Char;
i: Integer;
begin
Write('Input a new file name: ');
Readln(FileName);
// Check if file exists, warn user if it is already exist
if FileExists(FileName) then
begin
Write('File already exist, did you want to overwrite it? (y/n)');
Readln(Ans);
if upcase(Ans) = 'Y' then
ReadyToCreate:= True
else
ReadyToCreate:= False;
end
else // File does not exist
ReadyToCreate:= True;
if ReadyToCreate then
begin
// Link file variable (F) with physical file (FileName)
AssignFile(F, FileName);
Rewrite(F); // Create new file for writing
Writeln('Please input file contents line by line, '
, 'when you finish write % then press enter');
i:= 1;
repeat
Write('Line # ', i, ':');
Inc(i);
Readln(Line);
if Line <> '%' then
Writeln(F, Line); // Write line into text file
until Line = '%';
CloseFile(F); // Release F and FileName connection, flush buffer
end
else // file already exist and user does not want to overwrite it Writeln('Doing nothing');
Write('Press enter key to close..');
Readln;
end.
In this example, we have used many important things:
1. Boolean type:
ReadyToCreate: Boolean;
This type can hold only one of two values: either True or False. These values can be used directly in if condition, while loop or repeat loop.
In the previous example, we have used the if condition like this:
if Marks[i] > Max then
Which eventually turns to True or False.
2. UpCase function:
if upcase(Ans) = 'Y' then
This statement is executed when the file exists. The program will warn the user about overwriting an existing file. If he/she wants to continue, then he/she should enter a lowercase y or capital Y. The UpCase function will convert the character into a capital letter if it is lowercase.
3. Rewrite procedure:
Rewrite(F); // Create new file for writing
The Rewrite procedure is used to create a new empty file. If the file already exists, it will be erased and overwritten. It also opens the file for writing only in case of text files.
4. Writeln(F, .. ) procedure:
Writeln(F, Line); // Write line into text file
This procedure is used to write string or variables in text file, and appends them with end of line characters, which are a carriage return/line feed combination (CR/LF), represented as the characters for the numbers 13 and 10 respectively.
These characters can not be displayed in a console window, but it will move the screen display cursor to a new line.
5. Inc procedure:
Inc(i);
This procedure increases an integer variable by one. It is equivalent to the statement: i:= i + 1;
6. CloseFile procedure:
CloseFile(F); // Release F and FileName connection, flush buffer
As we mentioned earlier, the CloseFile procedure releases a file in the operating system. In addition, it has an additional job when writing to a text file, which is flushing the writing buffer.
Buffering of text files is a feature that makes dealing with text files faster. Instead of writing a single line or character directly to disk or any other storage media ( which is very slow compared with writing into memory), the application will write these entries into a memory buffer. When the buffer reaches its full size, it will be flushed (forced to be written) into permanent storage media like a hard disk. This operation makes writing faster, but it will add the risk of losing some data (in the buffer) if the power is suddenly lost. To minimize data loss, we should close the file immediately after finishing writing to it, or calling the Flush procedure to flush the buffer explicitly.
Appending to a text file
In this example, we want to open an existing text file for writing at the end of it, without removing its original contents using the procedure Append.
Add to text file program
var
FileName: string;
F: TextFile;
Line: string;
i: Integer;
begin
Write('Input an existed file name: ');
Readln(FileName);
if FileExists(FileName) then
begin
// Link file variable (F) with physical file (FileName)
AssignFile(F, FileName);
Append(F); // Open file for appending
Writeln('Please input file contents line by line',
'when you finish write % then press enter');
i:= 1;
repeat
Write('Line # ', i, ' append :');
Inc(i);
Readln(Line);
if Line <> '%' then
Writeln(F, Line); // Write line into text file
until Line = '%';
CloseFile(F); // Release F and FileName connection, flush buffer
end
else
Writeln('File does not exist');
Write('Press enter key to close..');
Readln;
end.
After we run this application and enter an existing text file, we can view it with the cat / type commands, or by double clicking on it in its directory to see the appended data.
Random access files
As we mentioned earlier, the second type of file according to an access type perspective is Random access , or direct access. This type of file has a fixed size record, so that we can jump to any record for reading or writing at any time.
There are two types of random access files: typed files and untyped files.
Typed files
Typed files are used for files that contain the same type of data that has the same size of records, for example, if a file contains records of Byte type, that means the record size = 1 byte. If the file contains real numbers ( Single), that means all record sizes are 4 bytes, etc.
In the next example, we will show how to use file of Byte:
Marks program
var
F: file of Byte;
Mark: Byte;
begin
AssignFile(F, 'marks.dat');
Rewrite(F); // Create file
Writeln('Please input students marks, write 0 to exit');
repeat
Write('Input a mark: ');
Readln(Mark);
if Mark <> 0 then // Don't write 0 value
Write(F, Mark);
until Mark = 0;
CloseFile(F);
Write('Press enter key to close..');
Readln;
end.
In this example, we have used this syntax to define a typed file:
F: file of Byte;
Which means: the file contains records of Byte data type, which can hold values from 0 to 255.
And we have created the file and opened it for writing using the Rewrite procedure: Rewrite(F); // Create file
Also we have used the function Write instead of Writeln to write records in the typed file: Write(F, Mark);
Writeln is not suitable for typed files, because it appends CR/LF characters on each line of written text, but Write stores the record as it is without any additions. In this case, if we have entered 10 records (of Byte), file size will be 10 bytes on disk.
In the next example, we will show how to display the previous file's contents: Reading student marks
program ReadMarks;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Classes, SysUtils
{ you can add units after this };
var
F: file of Byte;
Mark: Byte;
begin
AssignFile(F, 'marks.dat');
if FileExists('marks.dat') then
begin
Reset(F); // Open file
while not Eof(F) do
begin
Read(F, Mark);
Writeln('Mark: ', Mark);
end;
CloseFile(F);
end
else
Writeln('File (marks.dat) not found');
Write('Press enter key to close..');
Readln;
end.
In the next example, we will show how to append new records without deleting the existing data: Appending student marks program
program AppendMarks;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Classes, SysUtils
{ you can add units after this };
var
F: file of Byte;
Mark: Byte;
begin
AssignFile(F, 'marks.dat');
if FileExists('marks.dat') then
begin
FileMode:= 2; // Open file for read/write
Reset(F); // open file
Seek(F, FileSize(F)); // Go to beyond last record
Writeln('Please input students marks, write 0 to exit');
repeat
Write('Input a mark: ');
Readln(Mark);
if Mark <> 0 then // Don't write 0 value in disk
Write(F, Mark);
until Mark = 0;
CloseFile(F);
end
else
Writeln('File marks.dat not found');
Write('Press enter key to close..');
Readln;
end.
After running this program and entering new records, we can run it again to see the appended data.
Note that we have used Reset for opening the file for writing instead of the Rewrite procedure. Rewrite erases all data for existing files, and creates an empty file if the file does not exist, while Reset can only open an existing file without erasing its contents.
Also we have assigned 2 to the FileMode variable to indicate that we need to open the file for read/write access mode. 0 in FileMode means read only, 1 means write only, 2 ( Default) means read/write.
FileMode:= 2; // Open file for read/write
Reset(F); // open file
Reset puts the read/write pointer to the first record, and for that reason if we start to write records immediately, we will overwrite the old records, so we have used the Seek procedure to move the read/write pointer to the end of file. Seek can be used only with random access files.
If we try to access a record position that does not exist with the Seek procedure (for example, record number 100, while we have only 50 records), we will get an error.
We have also used the FileSize function, which returns the current record count in the file. It is used in combination with the Seek procedure to jump to the end of the file:
Seek(F, FileSize(F)); // Go to after last record
Note that this example can be used if the Student marks file already exists; if not, then we should run the first program (Storing Student Marks) because it uses Rewrite which can create new files.
We can combine both methods ( Reset and Rewrite) according to whether the file