Amikor az ember nagy adatmennyiséget másol, kissé lassúnak tűnnek a Norton vagy Intéző típusú másoló programok. A Dos Navigator ugyan megoldás lehet, de vannak emberek - többek között én is ilyen vagyok –, akiknek már a Dos Navigator nevének hallatán futkosni kezd a hátán a szőr.
Mit tehetünk hát, ha szeretnénk egy gyors másolóprogramot? Írjunk egyet (elvégre ez itt egy programozási rovat)!
Lássuk csak, mit is szeretnénk!
Egy olyan másoló programot szeretnénk írni, amely egyszerre több fájlt másol, és lehetőleg ezt jó gyorsan teszi. Ha megnézzük, a DN úgy másol, hogy beolvas a memóriába egy rakás fájlt, majd szépen kiírja a célhelyre. Ezt fogjuk mi is tenni.
A programnak első lépésben fel kell építenie egy fájl- és egy könyvtárlistát. A könyvtárlistára azért van szükség, mert ha könyvtárakat másolunk, akkor a célhelyen létre kell hozni a könyvtárakat. Az én verziómban két külön dinamikus listát használunk a fájl- és könyvtárnevek eltárolására. A listákat egy könyvtárfa-bejáró procedúra építi fel.
Második lépésben történik a másolás. A másolást addig tesszük, amíg ki nem fogyunk a fájlnevekből. A program szorgosan elkezdi olvasni a fájlokat, majd ha megtelt a memória, az egészet kimásolja winchesterre, a célterületre és kiüríti a memóriát.
Ha mindennel készen vagyunk, a memóriából kirúgjuk a fájllistát is.
Ez volna a program nagyvonalakban. Most lássuk, miként kell ezt megírni Pascalban!
Adattípusok
Type | Type kulcsszó |
PtreeList=^TTreelist;
TtreeList=Record Text: String[80]; Next: PtreeList; End; |
Könyvtárlistaelem
Ez a könyvtárlistát tartalmazó dinamikus lista leírása. A TtreeList Text mezőjében lesz a létrehozandó könyvtár neve (a forrás könyvtárhoz viszonyítva) |
Ppiece=^Tpiece;
Tpiece=Record Piece: Pointer; Size : Word; Next : Ppiece; End; |
Fájldarabka
Ilyen rekordokból álló lista lesz a memóriában egy fájl. A Piece pointer a fájl darabjának címét, a size a méretét és a Next a következő fájldarabka címét tartalmazza. |
PfileList=^Tfilelist;
TfileList=Record
Text: String[80]; FirstP: Ppiece; ActPos: LongInt; Ready: Boolean; saved: Boolean; Next: PfileList; End; |
Fájllistaelem
Ilyen rekordokban tároljuk a fájlokra jellemző információkat: a Text a fájl neve és útvonala (a forrás könyvtárhoz viszonyítva), a FirstP tartalmazza az első fájldarabka címét, az ActPos tartalmazza a fájlból eddig beolvasott hosszt, ha a ready mező értéke True, akkor ezt a fájlt már átmásoltuk, ha a saved mező értéke True, akkot a fájlt már létrehoztuk a célterületen. |
Procedure buildfilelist(cc:string; Var FTree: PTreeList;Var FFile: PFileList);
Var
LTree: PTreeList;
LFile: PFileList;
procedure fabejar(c: String);
Var
s: SearchRec;
t: String;
i: Integer;
P1: PFileList;
P2: PTreeList;
Begin
t:=c; if ((length(t)>3) and (t[length(t)]='\')) then
t:=copy(t,1,length(t)-1);
chdir(t);
if ioresult<>0 then exit;
New(P2);
P2^.Text:=copy(c,length(cc)+1,255);
P2^.Next:=Nil; if LTree<>Nil then LTree^.Next:=P2
Else FTree:=P2;
LTree:=P2;
s.name:='';
FindFirst('*.*',$3f,s);
While s.name<>'' do
Begin
if s.name[1]<>'.' then
Begin
if (s.attr and $10)<>0
then
Begin
{$I-}
chdir(s.name);
{$I+}
if ioresult=0
then
Begin
GetDir(0,t);
fabejar(t);
End;
End
Else
Begin
if (s.attr and
24)=0 then
Begin
if
c[length(c)]<>'\' then c:=c+'\';
New(P1);
P1^.FirstP:=Nil;
P1^.ActPos:=0;
P1^.ready:=false;
P1^.saved:=false;
P1^.Text:=copy(c,length(cc)+1,255)+s.name;
P1^.Next:=Nil;
if LFile<>Nil then LFile^.Next:=P1 Else FFile:=P1;
LFile:=P1;
End;
End;
End;
s.name:='';
FindNext(s);
End;
{$I-}
chdir('..');
{$I+}
if ioresult<>0 then exit;
End;
Begin
LTree:=FTree; if LTree<>Nil then While LTree^.Next<>Nil
do LTree:=LTree^.Next;
LFile:=FFile; if LFile<>Nil then While LFile^.Next<>Nil
do LFile:=LFile^.Next;
fabejar(cc);
End;
A bemenő paraméterek:
A rekurzív rutin, miután ellenőrizte, hogy a paraméterben megkapott könyvtárnév helyes-s beírja a paraméterét a könyvtárlista végére, majd a paraméterben megkapott könyvtárat végignézi, a fájlokat felveszi a fájllistába, ha könyvtárat talál, meghívja saját magát, de a könyvtár nevét adva paraméternek. Ennyi.
Maga a másolást végző rutin.
Procedure Copier(source,destination: String);
Var
FirstTree: PTreeList;
FirstFile: PFileList;
P1: PTreeList;
P2,P2a: PFileList;
P3,P3a: Ppiece;
size: Word;
LastP: Ppiece;
f: File;
Begin
A
program beállítja a fájl- és könyvtárlistát, majd meghívja a fabejáró procedúrát
FirstTree:=Nil; FirstFile:=Nil;
BuildFileList(source,FirstTree,FirstFile);
A
könyvtárak létrehozása a célhelyen
P1:=FirstTree;
if P1<>Nil then P1:=P1^.Next;
While P1<>Nil do
Begin
{$I-}
MkDir(destination+P1^.Text);
{$I+}
if ioresult<>0
then ;
P1:=P1^.Next;
End;
Itt
történik a másolás
P2:=FirstFile;
Elindulunk
a fájllista elejéről…
While P2<>Nil do
Begin
P2:=FirstFile;
Addig
olvasunk, amíg a fájllista végére érünk vagy elfogy a memóriánk.
While ((P2<>Nil)
and (maxavail>50000)) do
Begin
Megnyitjuk
az éppen következő fájlt.
Assign(f,source+P2^.Text);
filemode:=0; {megnyitás csak olvasásra}
Reset(f,1);
Előfordulhat,
hogy egy fájl nem fér be teljes egészében a memóriába. Ilyenkor az ActPos
mező értéke nem nulla,
hanem
az a szám, ameddig már megvan a célhelyen, vagyis a fájlt csak ActPos-tól
kezdődően kell beolvasni.
Seek(f,P2^.ActPos);
LastP:=Nil;
Itt olvassuk be az aktuális fájlt.
Addig olvasunk, amíg el nem fogy a memória vagy a fájl végére nem érünk.
A fájlt 49Kbyte-os darabokban olvassuk.
While ((Not Eof(f)) and (maxavail>50000)) do
Begin
Kiszámoljuk, hogy mekkora darabot kell
beolvasni a fájlból
if filesize(f)-filepos(f)>49152 then size:=49152 else size:=filesize(f)-filepos(f);
Memóriát
foglalunk a következő fájldarabnak (P3)
New(P3);
Az új
darabot hozzáfűzzük a már meglévő fájldarabkákhoz
P3^.Next:=Nil;
if LastP<>Nil then LastP^.Next:=P3 else P2^.FirstP:=P3;
LastP:=P3;
Beállítjuk az új darabka adatait
P3^.Size:=size;
Memóriát foglalunk az új fájldarabkához
tartozó fájldarabnak
GetMem(P3^.piece,size);
Beolvassuk
a fájl következő részét.
BlockRead(f,P3^.piece^,size);
Frissítjük
a fájlhoz tartozó információkat. A ready mező akkor lesz igaz, ha a fájlt
teljes egészében beolvastuk.
P2^.ActPos:=P2^.ActPos+size;
P2^.ready:=P2^.ActPos=filesize(f);
End;
Lezárjuk
az éppen aktuális fájlt, és továbblépünk a következőre.
Close(f);
P2:=P2^.Next;
Ha van
még memóriánk és a következő fájl sem NIL (P2) akkor a ciklus kezdődik
előről.
End;
A
beolvasott adatok kiírása
filemode:=2;
P2:=FirstFile;
A
ciklus addig megy, amíg van olyan fájlunk, amiből van egy darab a memóriában.
While (P2<>Nil)
and (P2^.ActPos<>0) do
Begin
Assign(f,destination+P2^.Text);
Megnyitjuk
a fájlt. Ha a saved mező értéke igaz, akkor a fájlnak már van darabja a
célhelyen vagyis
a fájlt
csak továbbírni kell, nem pedig létrehozni.
if P2^.saved then
Begin
Reset(f,1);
Seek(f,filesize(f));
End
Else
Begin
Ha a saved értéke hamis, akkor létrehozzuk
a fájlt a célhelyen és a saved-et igazra állítjuk, jelezve,
hogy ha legközelebb megnyitjuk a fájlt
akkor továbbírni kell, nem pedig létrehozni. Egyébként ennek
csak olyan
fájloknál van jelentősége, melyek nem fértek bele egyben a memóriába.
P2^.saved:=true;
ReWrite(f,1);
End;
Ha megnyitottuk
a fájlt, az első darabjától kezdve kimásoljuk a winchesterre.
P3:=P2^.FirstP;
While P3<>Nil do
Begin
BlockWrite(f,P3^.piece^,P3^.Size);
P3:=P3^.Next;
End;
Mikor
kiírtuk a fájlt a célhelyre, lezárjuk és nekiállunk a következőnek (már
ha van következő)
Close(f);
P2:=P2^.next;
End;
Nem
maradt más hátra, mint hogy kiürítsük a memóriát
a következőkben beolvasandó fájlok
számára.
Elindulunk az első fájltól és ha a fájl ready mezője igaz (vagyis teljes
egészében átmásoltuk),
töröljük
a fájlhoz tartozó darabkákat és a fájlt a fájllistából.
P2:=FirstFile;
{$B-}
While ((P2<>Nil)
and (P2^.ready)) do
Begin
Fájldarabkák törlése a memóriából
P3:=P2^.FirstP;
P2^.FirstP:=Nil;
While P3<>Nil do
Begin
FreeMem(P3^.Piece,P3^.Size);
P3a:=P3^.Next;
Dispose(P3);
P3:=P3a;
End;
Fájl kivétele a fájllistából
P2a:=P2;
P2:=P2^.Next;
Dispose(P2a);
FirstFile:=P2;
End;
{$B-}
A
memóriában szinte mindig marad egy fájl, amit csak félig sikerült beolvasni,
mert közben elfogyott a memória.
Ennek
a darabjait is ki kell rúgni a tárból mivel előfordulhat, hogy ha nem rúgjuk
ki, nem marad hely a következő
fájok
beolvasására.
if ((FirstFile<>Nil)
and (FirstFile^.ready=false) and (firstfile^.actpos<>0)) then
Begin
P3:=FirstFile^.FirstP;
FirstFile^.FirstP:=Nil;
While P3<>Nil do
Begin
FreeMem(P3^.Piece,P3^.Size);
P3a:=P3^.Next;
Dispose(P3);
P3:=P3a;
End;
End;
End;
End;
Néhány további információ:
Filemode változó
a filemode változó a pascal implicit (nem kell definiálni) változója,
ami a fájlkezelés módját szabályozza. Köztudott, hogy ha egy írásvédett
fájlt akarunk megnyitni, akkor a pascal a nem éppen barátságos FILE
ACCES DENIED - fájlelérés megtagadva hibaüzenettel
küldi el a programozót - és a felhasználót - melegebb égtájakra. Ezt a
problémát lehet úgy kiküszöbölni, hogy a filemode változót a fájl megnyitása
előtt 0-ra állítjuk jelezve a pascalnak, hogy mi csak olvasni szeretnénk
azt.
FILEMODE változó
0: Fájl megnyitás csak olvasásra
1: Fájl megnyitás csak írásra (ennek mi lehet az
értelme?)
2: Fájl megnyitása írásra és olvasásra (ez az alapértelmezés)
{$B+|-} direktíva
Ez a fordítódirektíva a logikai kifejezések kiértékelésének módját
szabályozza. Ha be van kapcsolva, akkor a Pascal teljesen kiértékeli a
logikai kifejezéseket, míg ha ki van kapcsolva, akkor a Pascal csak
addig töri magát, amíg egyértelműen el tudja dönteni a kifejezés végleges
értékét. Például egy AND kifejezésnek ha az első tagja hamis, akkor már
nem is kell törődni a második taggal. Gyakorlati haszna csak védett
módban szerintem van, segít megelőzni a Runtime
Error 216 - General Protection Fault típusú hibaüzeneteket.
Hát remélem elég érthetőre sikerült fogalmaznom a mondanivalómat. Ha valami kérdésed van a cikkel, vagy bármely más, programozással kapcsolatos témával kapcsolatban, írd meg email címünkre: PC-XUSER@FREEMAIL.C3.HU! Ha tudunk, szívesen írunk a témáról.
Ja, és még valami: Ha a programot protected módban fordítod be, képes lesz a számítógépedben található összes memóriát kihasználni és így, memóriádtól függően akár 128 Megás darabokban másolni a fájlokat.
A cikkhez tartozik egy forráskód is: COPIER.PAS
A forráskód ugyanezen másolóprogram néhány egyéb cafranggal kiegészítve
úgy, hogy a forrás- és célkönyvtárat paraméterben lehet neki átadni.