user741875
user741875

Reputation:

Getting File Sizes > And then Getting the Total Size?

This should be easy, but I cannot seem to get it right as I seem to be confusing myself and converting to and from strings, integers and floats and whatnot.

Basically, I am populating a TListView with FileNames in one column, and in another column returning the File Size to the corresponding FileName. I am using a rather neat function found from here, which looks like this:

function FileSizeStr ( filename: string ): string;
const
//   K = Int64(1000);     // Comment out this line OR
  K = Int64(1024);     // Comment out this line
  M = K * K;
  G = K * M;
  T = K * G;
var
  size: Int64;
  handle: integer;
begin
  handle := FileOpen(filename, fmOpenRead);
  if handle = -1 then
    result := 'Unable to open file ' + filename
  else try
    size := FileSeek ( handle, Int64(0), 2 );
    if size < K then result := Format ( '%d bytes', [size] )
    else if size < M then result := Format ( '%f KB', [size / K] )
    else if size < G then result := Format ( '%f MB', [size / M] )
    else if size < T then result := Format ( '%f GB', [size / G] )
    else result := Format ( '%f TB', [size / T] );
  finally
    FileClose ( handle );
  end;
end;

This returns values such as: 235.40 KB

So with the above, my TListView may be populated like so:

enter image description here

Now in the Label Data Size, I would like to return the Total Size of the Files in the Listview, so in this example, the values from the Size column would need adding up to return the Total Size, something like:

1.28 MB + 313.90 KB + 541.62 KB + 270.96 KB

Obviously it cannot be added on just like that, because the values contain decimal points, some values may be in Kb, and other in Mb etc. This is my problem, I cannot think of an easy solution to add (get) the Total Size of the Files, and then return it in the same formatted string as shown.

I would really appreciate some insight or tips how to work with this kind of data, I am just endlessly confusing myself with different conversions etc and not really sure which way to do this.

Many Thanks in advance :)

UPDATE 1

Following the advice from Marc B, I changed the function to the following which seems to work:

var
  iFileSize: Int64;

implementation

function GetSizeOfFile(FileName: string): Int64;
var
  Handle: Integer;
begin
  Handle := FileOpen(FileName, fmOpenRead);

  if Handle = -1 then
    MessageDlg('Unable to open file ' + FileName, mtError, [mbOk], 0)
  else try
    iFileSize := iFileSize + FileSeek(Handle, Int64(0), 2);
  finally
    FileClose(Handle);
  end;

  Result := iFileSize;
end;

function FormatFileSize(AValue: Int64): string;
const
  K = Int64(1024);
  M = K * K;
  G = K * M;
  T = K * G;
begin
  if AValue < K then Result := Format ( '%d bytes', [AValue] )
  else if AValue < M then Result := Format ( '%f KB', [AValue / K] )
  else if AValue < G then Result := Format ( '%f MB', [AValue / M] )
  else if AValue < T then Result := Format ( '%f GB', [AValue / G] )
  else Result := Format ( '%f TB', [AValue / T] );
end;

It may be useful for anyone else should they need it :)

UPDATE 2

Additionally, see the answer Ken White posted which provides more valuable information, and a cleaner update of the GetSizeOfFile function, which works great:

enter image description here

Upvotes: 4

Views: 6837

Answers (2)

Ken White
Ken White

Reputation: 125669

The easiest way would be to change your function to return the file size, and use a separate function to format the results.

I know you've already accepted an answer, but the updated code you posted has a couple of problems (one was in the original version, too).

First, your method of getting the file size is extremely slow, especially if you're going to be using this to list a lot of files. You're actually opening the file, moving the file pointer to the end of the file to get the size, and then closing the file. Also, this may fail if the file is open by another application exclusively.

Second, your new version of GetSizeOfFile has a logic error. You're adding to the global cumulative value every time (which is what you want), but you're also returning that new global value, which you don't want according to the sample image you posted.

Here's a replacement for GetSizeOfFile that should work for you, along with sample use:

function GetSizeOfFile( const FileName: String ): Int64;
var
  Rec : TSearchRec;
begin
  Result := 0;
  if (FindFirst(FileName, faAnyFile, Rec) = 0) then
  begin
    Result := Rec.Size;
    FindClose(Rec);
  end;
end;

Sample use:

var
  FileSize: Int64;
  FileSizeString: string;
begin
  { Whatever code  }
  FileSize := GetSizeOfFile(SomeFileName);
  iFileSize := iFileSize + NewSize;
  FileSizeString := FormatFileSize(NewSize);

  { Add your file to your ListView.}
end;

Upvotes: 2

Marc B
Marc B

Reputation: 360642

Separate the "get file information" from the "format the size string" into two separate functions. The file information function fetches the file size and adds it to a running total, THEN calls the formatting function to convert the simple integer into the "nice" string.

Upvotes: 6

Related Questions