Avatar billede angelod Novice
05. februar 2003 - 10:23 Der er 19 kommentarer og
1 løsning

Store filstørrelser i Delphi?

Jeg har det problem at jeg lige nu står og skal håndtere en fil på 4+ GB, og det kan jeg ikke med de funktioner jeg har nu, da de bruger Integer. Hvad kan jeg gøre for at få den rigtige filstørrelse? Det er jo lidt provokerende at få en størrelse på ca. minus 4 GB.

Er her nogen som har en løsning?
Avatar billede dcgeek Nybegynder
05. februar 2003 - 10:28 #1
lav din integer til longint.
Avatar billede angelod Novice
05. februar 2003 - 11:16 #2
Det vil da ikke hjælpe noget, da jeg for det første alligevel skal have filstørrelsen returneret fra systemet. Den funktion jeg kender er gennem FindFirst hvor der benyttes TSearchRec, og dens 'Size' property er en Integer.

For det andet, så er Integer det samme som Longint, i hvert fald i den Delphi jeg benytter (Delphi 7). Begge har en range der hedder: -2147483648..2147483647

Hvis jeg bare kunne skifte typen, så brugte jeg en Int64, men det kræver jo at alle funktionerne i systemet understøtter dette, og det gør de ikke..
Avatar billede siz23 Nybegynder
05. februar 2003 - 13:00 #3
kunne man ikke bare smide værdien over i en cardinal?
(32 bit unsigned integer).
Avatar billede angelod Novice
05. februar 2003 - 13:05 #4
Det kunne man måske nok, men så er man kun garanteret op til omkring 4GB, og hvad så når filen bliver større end dette? Det er en mulig løsning, men det er ikke en der holder på lang sigt. Desværre..
Avatar billede jpvj Nybegynder
05. februar 2003 - 13:07 #5
Kan godt se dit problem, men ingen løsning. Nu har du ikke skrevet noget om formatet af filen, men måske kunne man komme igennem med base:offset løsnignen:

myRecord : Array[1..1024] of Byte;
File of myRecord;

Dvs. du adresserer et record number og får således fat i ex. 1 kB ad gangen.

JP
Avatar billede angelod Novice
05. februar 2003 - 13:12 #6
Den sad jeg faktisk selv og rodede med, men så kommer problemet, hvordan får jeg den præcise størrelse? Der er jo en fejlmargin på 0-1023 bytes i denne løsning, og selvom det måske ikke lyder af så meget, så er det dog et problem i længden når filstørrelsen skal være nøjagtig. (Ja, folk kan være ret besværlige på det område)
Avatar billede stoney Nybegynder
05. februar 2003 - 14:02 #7
Jeg har ikke lige 4GB fil jeg kan teste på, men prøv nedenstående

procedure TForm1.Button1Click(Sender: TObject);
var
f: file of Byte;
size: int64;
begin

AssignFile(f, 'd:\fil.xxx');
Reset(f);
try
size := FileSize(f);
finally
CloseFile(f);
showmessage('Filen er '+ inttostr(size)+' kb' );
end;
end;

Stoney
Avatar billede angelod Novice
05. februar 2003 - 15:06 #8
Dette piner mig lidt, men jeg kan ud fra koden allerede nu se at det ikke vil virke, da FileSize returnerer en Integer, ikke en Int64. Jeg har heller ikke selv en 4 GB fil, da det er noget jeg laver for en anden.
Avatar billede loke-liscom Nybegynder
05. februar 2003 - 15:55 #9
function _FileSize(var f: TFileRec): Longint;
....
Avatar billede loke-liscom Nybegynder
05. februar 2003 - 15:58 #10
API-kaldet som filesize bruger....

DWORD GetFileSize(
  HANDLE hFile,
  LPDWORD lpFileSizeHigh
);

Din fil kan være op til 16777216 Terrabyte.... det må da være nok
Avatar billede doc404 Novice
05. februar 2003 - 16:11 #11
Du er nød til at bruge de Windows API I/O funktioner, der understøtter filer > 4GB.

GetFileSizeEx(),SetFilePointerEx() etc...

Du er nød til selv at lave prototyper - de er i hvert tilfælde ikke i min D5 Prof.


loke-liscom> GetFileSize returnerer i en DWORD - altså en alm integer.
Avatar billede hreiff Nybegynder
05. februar 2003 - 16:30 #12
Du kan gå ind og læse filen (det tager faktisk ikke så lang tid):

procedure TForm1.Button2Click(Sender: TObject);
const
  buflen = 1000;
var
  f: file;
  i: longint;
  NumRead: word;
  buf: array[1..buflen] of char;
begin
  i := -1;
  assignfile(f,'C:\TEMP\TEST.TXT');
  reset(f,1);
  repeat
    inc(i);
    blockread(f,buf,buflen,NumRead);
  until Numread <> buflen;
  closefile(f);
  showmessage(inttostr(i)+' tusind '+inttostr(Numread)+' bytes.');
end;

Du kan selvfølgelig også gange sammen (1000*i+Numread) hvis du har en stor nok variabel.
Avatar billede angelod Novice
05. februar 2003 - 17:45 #13
Ja, det er rigtigt nok at GetFileSize understøtter vildt store filer, men det hjælper jo ikke noget hvis FileSize returnerer en Integer, og _FileSize returnerer en Longint. De har jo samme størrelse, og samme range.. Måske en løsning der benytter de API kald som f.eks. GetFileSize, selvom jeg ikke helt kan gennemskue hvordan jeg får fat på det handle jeg skal bruge.. Har desværre ikke arbejdet vildt meget med WinAPI.
Avatar billede borrisholt Novice
06. februar 2003 - 10:04 #14
function FileSize(var F): Integer;

Retunere en integer, (32 bit signed), i hvertfald i D5

Hvis du skal være helt sikker på at få størelsen ud i en QWord (Quad Word), så skal du bruge En FindFirst.

altså sådan her :

function JBGetFileSize(const FileName : TFileName) : Int64;
var
  SearchRec : TSearchRec;
begin
  Result := -1;
  if FindFirst(FileName, FaAnyFile, SearchRec) = 0 then
  begin
    Result := SearchRec.FindData.nFileSizeHigh;
    Result := Result shl 32;
    Result := Result +  SearchRec.FindData.nFileSizeLow;
    FindClose(SearchRec);
  end;
end;


Desværre mangler Delphi en QWORD (mig bekendt) så du må nøjes med en Int64.

Jens B
Avatar billede borrisholt Novice
06. februar 2003 - 10:11 #15
eller lidt simplere :

function JBGetFileSize(const FileName : TFileName) : Int64;
var
  SearchRec : TSearchRec;
begin
  Result := -1;
  if FindFirst(FileName, FaAnyFile, SearchRec) = 0 then
  begin
    Result := SearchRec.FindData.nFileSizeHigh shl 32 +  SearchRec.FindData.nFileSizeLow;
    FindClose(SearchRec);
  end;
end;
Avatar billede doc404 Novice
06. februar 2003 - 10:31 #16
Her her hvad du skal bruge...

function GetFileSizeEx(hFile: THandle; PLargeInteger: Pointer): DWORD;  stdcall; external kernel32;

function SetFilePointerEx(hFile: THandle; lDistanceToMove: LARGE_INTEGER; lpDistanceToMoveHigh: PLargeInteger; dwMoveMethod: DWORD): DWORD; stdcall; external kernel32;

procedure Test;
var
  hFile    : THANDLE;
  FileSize  : LARGE_INTEGER;
  Buffer    : array[0..2047] of byte;
  dwCount  : DWORD;
  FilePos  : LARGE_INTEGER;
begin
  hFile := CreateFile('c:\whatever.fil',GENERIC_READ or GENERIC_WRITE,0,0,OPEN_EXISTING,0,0);
  if hFile = INVALID_HANDLE_VALUE then
    begin
      ShowMessage(Format('Error : %d',[GetLAstError]));
    end;
  GetFileSizeEx(hFile,@FileSize);
  ShowMEssage(IntToStr(FileSIze.QuadPart));

  FilePos.QuadPart := 200;
  SetFilePointerEx(hFile,FilePos,@FilePos,FILE_BEGIN);

  ReadFile(hFile,Buffer,100,dwCount,nil);
  if dwCount <> 100 then SHowMEssage('Hmm kunne ikke læse 100 bytes');

  WriteFile(hFIle,Buffer,100,dwCount,nil);
  if dwCount <> 100 then SHowMEssage('Hmm kunne ikke skrive 100 bytes');

  CloseHandle(hFile);
end;
Avatar billede borrisholt Novice
06. februar 2003 - 10:34 #17
Den retunerer stadig kun en DWORD !!!

Jens B
Avatar billede angelod Novice
06. februar 2003 - 10:49 #18
Takker Jens. Det var lige det der skulle til. Hvis jeg havde nærlæst den del af manualen havde jeg sikkert også fundet det, men skidt, nu fandt du det, og jeg kan komme videre. ^_^

Tak til alle jer andre som også kom med forslag, flere af dem meget gode. I er alle kanon! :o)
Avatar billede doc404 Novice
06. februar 2003 - 11:04 #19
Fejl i prototypen på GetFileSizeEx .. Returværdien er en BOOL...

Undre mig en smule over afvisningen af svaret...
Avatar billede angelod Novice
06. februar 2003 - 11:07 #20
Ganske simpelt den grund, at Jens Borrisholt gav mig det svar jeg bedst kunne bruge. Det kan integreres direkte, og returnerer en Int64, nøjagtig som jeg skrev ville være lækkert.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester