Reputation:
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:
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:
Upvotes: 4
Views: 6837
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
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