File Handling In Pascal/fi

From Free Pascal wiki
Revision as of 10:28, 28 December 2017 by Djzepi (talk | contribs)
Jump to navigationJump to search

العربية (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ä.

I/O-rutiineja

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.

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.

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