File Handling In Pascal/fi
│
العربية (ar) │
English (en) │
español (es) │
suomi (fi) │
français (fr) │
日本語 (ja) │
русский (ru) │
中文(中国大陆) (zh_CN) │
中文(臺灣) (zh_TW) │
Tiedostojen käsittely
Yleistä
Jokaisen ohjelmoijan tarvitsee tietää, miten käsitellään tiedostoja. Tiedostojen avulla säilytetään tietoja (dataa) eli tallennetaan tietoja niin, että ne voidaan hakea myöhemmin, ilman että niitä on luotava uudelleen. Tiedostojen avulla voidaan tallentaa käyttäjän asetukset, virhelokit, mittaus- tai laskentatulokset ja paljon muuta. Tämä sivu kertoo tiedostojen käsittelyn perusteet.
Vanhempi aliohjelmallinen tapa
Kun käytetään tiedostoja perinteisellä tavalla Pascalissa, voidaan käyttää TextFile tiedostotyyppiä (joka on yksinkertaisesti tekstiä sisältävä tiedosto: tekstitiedosto) tekstin tallentamiseen. Jokainen rivi päättyy rivin loppumerkkiin ( LineEnding ). Tämän tyyppisen tiedostoon voidaan tallentaa merkkijonojen lisäksi myös numeroita, jotka on muotoiltu eri tavoin. Nämä tiedostot voidaan avata ja muokata Lazarus IDE:ssä tai jossain muussa tekstieditorissa.
Tiettyjä tarkoituksia varten voidaan luoda oma tiedostotyyppi, joka voi tallentaa vain yhden tyyppistä dataa. Esimerkiksi:
...
type
TIntegerFile = file of integer; // Voidaan kirjoittaa vain kokonaislukuja tiedostoon
TExtendedFile = file of extended; // Voidaan kirjoittaa vain reaalilukuja tiedostoon
TCharFile = file of char; // Voidaan kirjoittaa vain yksittäisiä char-tyyppisiä merkkejä tiedostoon
I/O virheenkäsittely
I/O-virheenkäsittelyn lippu kertoo kääntäjälle, miten käsitellä virheetilanteita: nostetaan poikkeus tai tallennetaan I/O- käsittelyn tulos IOResult-muuttujalle. I/O-virheenkäsittelyn lippu on ohje kääntäjälle. Se voidaan ottaa käyttöön tai poistaa se käytöstä:
{$I+} // Virheet johtavat EInOutError-poikkeukseen (oletusarvo)
{$I-} // Poista I/O-virheet: jolloin tarkistetaan virhekoodi IOResult-muuttujasta
I/O-virheiden ({$ I-}) poistaminen vie tiedostotoimintojen tulokset IOResult-muuttujalle. Tämä on cardinal (numero) tyyppinen arvo . Eri numerot merkitsevät erilaisia virheitä. Joten kannattaa tarkistaa erilaisten virheiden dokumentaatiot [1].
Tiedostonkäsittelyn aliohjelmat
Nämä tiedoston käsittelyn aliohjelmat ja funktiot sijaitsevat System-käännösyksikössä. Katso lisätietoja FPC: n dokumentaatiosta: Reference for 'System' unit.
- AssignFile - Anna tiedoston nimi
- Append - Avaa olemassa olevan tiedoston tietojen lisäämiseksi tiedoston loppuun ja muokkaamalla sitä
- BlockRead - Lukee dataa (typittömästä) tiedostosta muistiin
- BlockWrite - Kirjoittaa dataa muistista (tyypittömään) tiedostoon
- CloseFile - Sulkee avatun tiedoston
- EOF - Tiedoston lopun tarkistus
- Erase - Poistaa tiedoston levyltä
- FilePos - Kertoo paikan tiedostossa
- FileSize - Kertoo tiedoston koon
- Flush - Tyhjentää tiedostopuskurin levylle.
- IOResult - Palauttaa viimeisen tiedoston IO-toiminnon tuloksen
- Read - Lukee tekstitiedostosta
- ReadLn - Lukee tekstitiedostosta ja siirtyy seuraavaalle riville
- Reset - Avaa tiedoston lukemista varten
- Rewrite - Luo tiedoston kirjoittamista varten
- Seek - Vaihtaa sijaintia tiedostossa
- SeekEOF - Aseta sijainti tiedoston loppuun
- SeekEOLn - Aseta sijainti rivin loppuun
- Truncate - Leikkaa tiedosto vain sijaintiin asti
- Write - Kirjoita muuttuja tiedostoon
- WriteLn - Kirjoita muuttuja tekstitiedostoon ja siirry uudelle riville
Esimerkki
Täydellinen esimerkki TextFile-tyyppisen tekstitiedoston käsittelystä:
program CreateFile;
uses
Sysutils;
const
C_FNAME = 'textfile.txt';
var
tfOut: TextFile;
begin
// Asetetaan luotavalle tiedostolle nimi
AssignFile(tfOut, C_FNAME);
// Käytä poikkeuksia virheiden varalta (tämä on oletusarvo, joten sitä ei ehdottomasti vaadita)
{$I+}
// Tee tiedostojen luominen, try/except lohkossa käsittellään virheet
try
// Luo tiedosto, kirjoita jotain tekstiä siihen ja sulje se.
rewrite(tfOut);
writeln(tfOut, 'Hello textfile!');
writeln(tfOut, 'The answer to life, the universe and everything: ', 42);
CloseFile(tfOut);
except
// Jos tapahtui virhe (/poikkeus) niin syy löytyy täältä
on E: EInOutError do
writeln('File handling error occurred. Details: ', E.ClassName, '/', E.Message);
end;
// Anna palautetta ja odota näppäimen painamista
writeln('File ', C_FNAME, ' created if all went ok. Press Enter to stop.');
readln;
end.
Nyt voidaan avata tiedosto millä tahansa tekstieditorilla ja nähdä yllä olevan tekstin kirjoitettuna siihen! Voidaan myös testata virheenkäsittelyä suorittamalla ohjelman kerran ja asettamalla tiedosto vain luettavaksi ja suorittamalla ohjelma uudelleen.
Huomaa, että poikkeustoimintoa käytettiin, sillä se on helppo tapa toimia kun on useita tiedostoja ja käsitellä virheitä. Voidaan käyttää myös {$ I-}, mutta silloin pitäisi tarkistaa IOResult jokaisen operaation jälkeen ja muokata seuraavaa operaatiota.
Seuraavassa kerrotaan, kuinka tekstiä lisätään lisää tekstitiedostoon:
program AppendToFile;
uses
Sysutils;
const
C_FNAME = 'textfile.txt';
var
tfOut: TextFile;
begin
// Asetetaan tiedoston nimi, johon lisätään tekstiä
AssignFile(tfOut, C_FNAME);
// Embed the file handling in a try/except block to handle errors gracefully
try
// Open the file for appending, write some more text to it and close it.
append(tfOut);
writeln(tfOut, 'Hello again textfile!');
writeln(tfOut, 'The result of 6 * 7 = ', 6 * 7);
CloseFile(tfOut);
except
on E: EInOutError do
writeln('File handling error occurred. Details: ', E.Message);
end;
// Give feedback and wait for key press
writeln('File ', C_FNAME, ' might have more text. Press enter to stop.');
readln;
end.
Tekstiedoston lukeminen:
program ReadFile;
uses
Sysutils;
const
C_FNAME = 'textfile.txt';
var
tfIn: TextFile;
s: string;
begin
// Give some feedback
writeln('Reading the contents of file: ', C_FNAME);
writeln('=========================================');
// Set the name of the file that will be read
AssignFile(tfIn, C_FNAME);
// Embed the file handling in a try/except block to handle errors gracefully
try
// Open the file for reading
reset(tfIn);
// Keep reading lines until the end of the file is reached
while not eof(tfIn) do
begin
readln(tfIn, s);
writeln(s);
end;
// Done so close the file
CloseFile(tfIn);
except
on E: EInOutError do
writeln('File handling error occurred. Details: ', E.Message);
end;
// Wait for the user to end the program
writeln('=========================================');
writeln('File ', C_FNAME, ' was probably read. Press enter to stop.');
readln;
end.
Olio tyyli
Edellä mainittujen perinteisteisten tiedostojen käsittelyn rutiinien lisäksi on olemassa oliotyyli, joka käyttää tietovirtojen käsitettä (- dataa) korkeammalla abstraktiotasolla. Tämä tarkoittaa, että tiedot voidaan lukea tai kirjoittaa mihin tahansa paikkaan (levy, muisti, laitteisto-portit jne.) Yhdellä yhtenäisellä tavalla.
Lisäksi useimmilla merkkijonojen käsittelyluokilla on mahdollisuus ladata ja tallentaa sisältöä tiedostoon. Näitä menetelmiä kutsutaan yleensä nimellä SaveToFile ja LoadFromFile. Monet muut oliot (kuten Lazarus grid:t) ovat samanlaisia toimintoja, kuten Lazaruksen datasets (DBExport). Kannattaa katsoa dokumentaatiota tai lähdekoodia ennen kuin yrittää tehdä omia tallennus- / latausrutiineita.
Yleinen tapa kaikille tyypeille
Tiedostojen avaamisessa voidaan käyttää TFileStream-luokkaa. Tähän luokkaan on kapseloitu aliohjelmat FileOpen, FileCreate, FileRead, FileWrite, FileSeek ja FileClose. Kyseinen luokka löytyy SysUtils käännösyksiköstä.
Tässä on hyvin yksinkertainen esimerkki yhden tiedoston lisäämisestä toiseen tietovirtojen (stream) avulla. Se vastaa edellä mainittua lisäystä, mutta paljon yksinkertaisempaa:
program inoutstream;
uses
classes,sysutils;
var
Instream,OutStream:TFilestream;
begin
if (ParamCount = 2) and
FileExists(ParamStr(1)) and
FileExists(ParamStr(2)) then
begin
instream := TFilestream.Create(ParamStr(1), fmOpenRead);
try
outstream :=TFilestream.Create(Paramstr(2), fmOpenwrite);
try
outstream.position := Outstream.size;
outstream.copyfrom(instream,0);// appends
finally
instream.free;
end;
finally
outstream.free;
end;
end else writeln('use: inoutstream <infile> <outfile>');
end.
Alla olevassa esimerkissä huomaa, kuinka tiedostojen käsittelytoiminta rajataan try ... except-lohkolla, jossaa virheet käsitellään oikein samalla tavoin kuin perinteisillä tiedostojen käsittelyrutiineilla (kuten ei sommitella esimerkkiä, jossa fsOut.writeä ei ole sijoitettu try ... finally lohkoon).
program WriteBinaryData;
{$mode objfpc}
uses
Classes, Sysutils;
const
C_FNAME = 'binarydata.bin';
var
fsOut : TFileStream;
ChrBuffer: array[0..2] of char;
begin
// Set up some random data that will get stored
ChrBuffer[0] := 'A';
ChrBuffer[1] := 'B';
ChrBuffer[2] := 'C';
// Catch errors in case the file cannot be created
try
// Create the file stream instance, write to it and free it to prevent memory leaks
fsOut := TFileStream.Create( C_FNAME, fmCreate);
fsOut.Write(ChrBuffer, sizeof(ChrBuffer));
fsOut.Free;
// Handle errors
except
on E:Exception do
writeln('File ', C_FNAME, ' could not be created because: ', E.Message);
end;
// Give feedback and wait for key press
writeln('File ', C_FNAME, ' created if all went ok. Press Enter to stop.');
readln;
end.
Voidaan myös ladata koko tiedosto muistiin, jos sen koko on suhteellisesti pienempi kuin käytettävissä oleva järjestelmämuisti. Suuremmat koot saattavat toimia, mutta käyttöjärjestelmä alkaa käyttää sivutus / swap-tiedostoa, mikä tekee siitä hyödyttömän suorituskyvyn näkökulmasta.
program ReadBinaryDataInMemoryForAppend;
{$mode objfpc}
uses
Classes, Sysutils;
const
C_FNAME = 'binarydata.bin';
var
msApp: TMemoryStream;
begin
// Set up the stream
msApp := TMemoryStream.Create;
// Catch errors in case the file cannot be read or written
try
// Read the data into memory
msApp.LoadFromFile(C_FNAME);
// Seek the end of the stream so data can be appended
msApp.Seek(0, soEnd);
// Write some arbitrary data to the memory stream
msApp.WriteByte(68);
msApp.WriteAnsiString('Some extra text');
msApp.WriteDWord(671202);
// Store the data back on disk, overwriting the previous contents
msApp.SaveToFile(C_FNAME);
// Handle errors
except
on E:Exception do
writeln('File ', C_FNAME, ' could not be read or written because: ', E.Message);
end;
// Clean up
msApp.Free;
// Give feedback and wait for key press
writeln('File ', C_FNAME, ' was extended if all went ok. Press Enter to stop.');
readln;
end.
</synyaxhighlight>
Jos on useampia Gb:n tiedostoja, kannattaa lukea esimerkiksi 4096 tavua puskuriin (kannattaa käyttää useita tiedostoklustereita tai lohkokokoja) ja tehdä jotain kunkin puskurin lukiessa tietoja.
<syntaxhighlight lang=pascal>
var
TotalBytesRead, BytesRead : Int64;
Buffer : array [0..4095] of byte; // or, array [0..4095] of char
FileStream : TFileStream;
try
FileStream := TFileStream.Create;
FileStream.Position := 0; // Ensure you are at the start of the file
while TotalBytesRead <= FileStream.Size do // While the amount of data read is less than or equal to the size of the stream do
begin
BytesRead := FileStream.Read(Buffer,sizeof(Buffer)); // Read in 4096 of data
inc(TotalBytesRead, BytesRead); // Increase TotalByteRead by the size of the buffer, i.e. 4096 bytes
// Do something with Buffer data
end;
Tiedoston kopiointi
Edellä esitetyllä tavalla voidaan toteuttaa yksinkertaisen tiedoston kopiointi-toiminnon (FreePascal:n RTL ei ole sellaista, tosin Lazaruksen kirjastoissa on kopiotiedosto )
program FileCopyDemo;
// Demonstration of FileCopy function
{$mode objfpc}
uses
classes;
const
fSource = 'test.txt';
fTarget = 'test.bak';
function FileCopy(Source, Target: string): boolean;
// Copies source to target; overwrites target.
// Caches entire file content in memory.
// Returns true if succeeded; false if failed.
var
MemBuffer: TMemoryStream;
begin
result := false;
MemBuffer := TMemoryStream.Create;
try
MemBuffer.LoadFromFile(Source);
MemBuffer.SaveToFile(Target);
result := true
except
//swallow exception; function result is false by default
end;
// Clean up
MemBuffer.Free
end;
begin
If FileCopy(fSource, fTarget)
then writeln('File ', fSource, ' copied to ', ftarget)
else writeln('File ', fSource, ' not copied to ', ftarget);
readln()
end.
Tekstitiedostojen käsittely (TStringList)
Yleensä tekstitiedostojen avulla voit käyttää TStringList-luokkaa kun ladataan koko tiedosto muistiin ja päästä helposti sen riveihin. Tietenkin voidaan myös kirjoittaa StringList takaisin tiedostoon:
program StringListDemo;
{$mode objfpc}
uses
Classes, SysUtils;
const
C_FNAME = 'textfile.txt';
var
slInfo: TStringList;
begin
// Create an instance of the string list to handle the textfile
slInfo := TStringList.Create;
// Embed the file handling in a try/except block to handle errors gracefully
try
// Load the contents of the textfile completely in memory
slInfo.LoadFromFile(C_FNAME);
// Add some more contents
slInfo.Add('An extra line appended to the text');
slInfo.Add('And another one.');
slInfo.Add('Let''s stop here.');
slInfo.Add('It is now ' + DateTimeToStr(now));
// And write the contents back to disk, replacing the original contents
slInfo.SaveToFile(C_FNAME);
except
// If there was an error the reason can be found here
on E: EInOutError do
writeln('File handling error occurred. Reason: ', E.Message);
end;
// Clean up
slInfo.Free;
// Give feedback and wait for key press
writeln('File ', C_FNAME, ' updated if all went ok. Press Enter to stop.');
readln;
end.
Demo: Tallenna yksi merkkijono tiedostoon
Jotta voidaan kirjoittaa yksi merkkijonon tietovirtaan (stream), kannattaa käyttää alla määritettyä menettelytapaa. Huomaa, että Free Pascalin stringit eli merkkijonot voivat olla erittäin pitkiä, ja tämä on myös hyödyllinen tapa kirjoittaa suuria tekstitiedostoja tiedostoon.
program SaveStringToPathDemo;
{$mode objfpc}
uses
Classes, sysutils;
const
C_FNAME = 'textstringtofile.txt';
// SaveStringToFile: funktio tallentaa merkkijonon tiedostoon.
// Jos funktio palauttaa true arvon niin merkkijono kirjoitettiin ok.
// Jos ei niin tapahtui jonkinlainen virhe.
function SaveStringToFile(theString, filePath: AnsiString): boolean;
var
fsOut: TFileStream;
begin
// By default assume the writing will fail.
result := false;
// Write the given string to a file, catching potential errors in the process.
try
fsOut := TFileStream.Create(filePath, fmCreate);
fsOut.Write(theString[1], length(theString));
fsOut.Free;
// At his point it is known that the writing went ok.
result := true
except
on E:Exception do
writeln('String could not be written. Details: ', E.ClassName, ': ', E.Message);
end
end;
//
// Main program
//
begin
// Try to save a simple textstring in a file and give feedback of sucess.
if SaveStringToFile('>> this text gets stored <<', C_FNAME) then
writeln('Text succesfully written to a file')
else
writeln('Writing text to a file failed.');
// Wait for the user to press Enter
readln
end.
Tiedoston käsittely ISO Moodissa
Komentoriviparametrit
Ensimmäinen vaihtoehto ulkoisten tiedostojen määrittämiseksi ISO-tilassa on komentoriviparametrien avulla niiden ulkoisen järjestyksen mukaan. Esimerkki:
program isotest1 (file1, file2);
var
file1: text;
file2: file of integer;
begin
reset(file1);
readln(file1);
rewrite(file2);
write(file2, 5);
end.
Ohjelma kootaan fpc -Miso isotest1.pas
ja suoritetaan ./isotest1 externa1l.txt external2.dat
. tiedostot1 on määritetty external1.txt: lle ja file2: lle external2.dat: lle. Ilman komentoriviparametreja tiedostot liitetään stdin ja stdout. Niin kauan kuin vain yksi syöttötiedosto ja yksi lähtö, sama tehtävä voidaan saavuttaa tällä komennolla: ./isotest1 <externa1l.txt >external2.dat
. tiedosto1 pitää olla olemassa ennen suoritusta, tiedosto2 luodaan sen aikana.
Läpinäkyvät tiedostonimet
Toinen vaihtoehto on läpinäkyvät tiedostonimet, joka saavutetaan kääntäjän asetuksella -Sr. Esimerkki:
program isotest2 (file1, file2);
var
file1: text;
file2: text;
begin
reset(file1);
readln(file1);
rewrite(file2);
write(file2, 5);
end.
Jos ohjelma on käännetty fpc -Miso -Sr isotest2.pas
ja suoritetaan ilman komentoriviparametreja, ulkoiset tiedostot FILE1.txt ja FILE2.txt luetaan ja kirjoitetaan. Huomaa sisäisten tiedostonimien muunnokset isoiksi kirjaimiksi. Tiedostoja, joilla on eri tiedostonimet, voidaan edelleen käyttää komentoriviparametrien avulla.
Katso myös
- CopyFile Lazaruksen funktio (käytettävissä myös komentorivillä), joka kopioi tiedostot
- File
- Kuinka käytetään avaa-dialogia
- Kuinka käytetään tallenna-dialogia