Recent

Author Topic: [Solved] - Formatting a file size to something more human readable  (Read 2941 times)

zxandris

  • Full Member
  • ***
  • Posts: 170
I've made a function that takes a size and returns a string accordingly.  It's below and appears to work, but I'm not entirely sure it's totally accurate and I do wonder if there's a more efficient way of doing it.  I've done it using string conversion but i wonder if using a float and format stirng might work better?  But  have no idea how to use that, or rather i don't know the formatting commands, would anyone be willing to cast an eye and see if this can be improved please?

Code: Pascal  [Select][+][-]
  1. function strFormatSize(const size : Int64) : String;
  2. const
  3.      KB = 1024;
  4.      MB = 1048576;
  5.      GB = 1073741824;
  6. var
  7.    rtn : String;
  8.    sze : Int64;
  9.    md : Int64;
  10.    mdStr : String;
  11. begin
  12.      rtn := '0 bytes';
  13.      if Size>0 then
  14.      begin
  15.          if size>=gb then
  16.          begin
  17.               sze := Size div GB;
  18.               md := size mod GB;
  19.               mdStr := IntToStr(md);
  20.               if length(mdStr)>2 then mdStr := leftStr(mdStr, 2);
  21.               rtn := CommaTHou(sze) + '.' + mdStr + ' gb';
  22.          end else if size>=MB then
  23.          begin
  24.               sze := size div MB;
  25.               md := size mod MB;
  26.               mdStr := IntToStr(md);
  27.               if length(mdStr)>2 then mdStr := leftStr(mdStr, 2);
  28.               rtn := CommaTHou(sze) + '.' + mdStr + ' mb';
  29.          end else if size>=KB then
  30.          begin
  31.               sze := size div KB;
  32.               md := size mod KB;
  33.               mdStr := IntToStr(md);
  34.               if length(mdStr)>2 then mdStr := leftStr(mdStr, 2);
  35.               rtn := CommaTHou(sze) + '.' + mdStr + ' kb';
  36.          end else
  37.          begin
  38.               sze := size;
  39.               rtn := CommaThou(sze) + ' bytes';
  40.          end;
  41.      end else rtn := CommaThou(size) + ' bytes';
  42.      result := rtn;
  43. end;
  44.  

GetSize is essentially just a bridge to the system command, and commathou does what it sounds like it does :).
« Last Edit: October 17, 2023, 05:25:30 pm by zxandris »

balazsszekely

  • Guest
Re: Formatting a file size to something more human readable
« Reply #1 on: October 16, 2023, 12:56:26 pm »
@zxandris
Quote
I've made a function that takes a size and returns a string accordingly.  It's below and appears to work, but I'm not entirely sure it's totally accurate and I do wonder if there's a more efficient way of doing it.  I've done it using string conversion but i wonder if using a float and format stirng might work better?  But  have no idea how to use that, or rather i don't know the formatting commands, would anyone be willing to cast an eye and see if this can be improved please?
Here you go:
Code: Pascal  [Select][+][-]
  1. function strFormatSize(const ASize: Int64): String;
  2. const
  3.   KB = 1024;
  4.   MB = 1024 * KB;
  5.   GB = 1024 * MB;
  6. begin
  7.   if ASize < KB then
  8.     Result := FormatFloat('#,##0 Bytes', ASize)
  9.   else if ASize < MB then
  10.     Result := FormatFloat('#,##0.0 KB', ASize / KB)
  11.   else if ASize < GB then
  12.     Result := FormatFloat('#,##0.0 MB', ASize / MB)
  13.   else
  14.     Result := FormatFloat('#,##0.0 GB', ASize / GB);
  15. end;  

zxandris

  • Full Member
  • ***
  • Posts: 170
Re: Formatting a file size to something more human readable
« Reply #2 on: October 16, 2023, 12:58:19 pm »
That was admirably quick! Thanks!  That looks much simplier and far more efficient than my code.

Thanks,

alpine

  • Hero Member
  • *****
  • Posts: 1410
Re: Formatting a file size to something more human readable
« Reply #3 on: October 16, 2023, 12:59:37 pm »
@zxandris
What is intended to be after the decimal dot in the result of your function?
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

zxandris

  • Full Member
  • ***
  • Posts: 170
Re: Formatting a file size to something more human readable
« Reply #4 on: October 16, 2023, 01:01:06 pm »
Like on windows it shows say 1.4mb I wanted to get that, or close to it.  Obviously as accurate to the file size as I could.

alpine

  • Hero Member
  • *****
  • Posts: 1410
Re: Formatting a file size to something more human readable
« Reply #5 on: October 16, 2023, 01:06:54 pm »
Like on windows it shows say 1.4mb I wanted to get that, or close to it.  Obviously as accurate to the file size as I could.
IMHO the:
Code: Pascal  [Select][+][-]
  1. md := size mod MB;
will result to value in range 0..1048575, not 0..99. Same for others.
« Last Edit: October 16, 2023, 01:08:42 pm by alpine »
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

zxandris

  • Full Member
  • ***
  • Posts: 170
Re: Formatting a file size to something more human readable
« Reply #6 on: October 16, 2023, 01:16:25 pm »
@zxandris
Quote
I've made a function that takes a size and returns a string accordingly.  It's below and appears to work, but I'm not entirely sure it's totally accurate and I do wonder if there's a more efficient way of doing it.  I've done it using string conversion but i wonder if using a float and format stirng might work better?  But  have no idea how to use that, or rather i don't know the formatting commands, would anyone be willing to cast an eye and see if this can be improved please?
Here you go:
Code: Pascal  [Select][+][-]
  1. function strFormatSize(const ASize: Int64): String;
  2. const
  3.   KB = 1024;
  4.   MB = 1024 * KB;
  5.   GB = 1024 * MB;
  6. begin
  7.   if ASize < KB then
  8.     Result := FormatFloat('#,##0 Bytes', ASize)
  9.   else if ASize < MB then
  10.     Result := FormatFloat('#,##0.0 KB', ASize / KB)
  11.   else if ASize < GB then
  12.     Result := FormatFloat('#,##0.0 MB', ASize / MB)
  13.   else
  14.     Result := FormatFloat('#,##0.0 GB', ASize / GB);
  15. end;  

So since I'm trying to learn what does FormatFloat('#,##0.0 MB', ASize / MB) actually do... I'm hoping that it's outputting a file size rather like you see in explorer but what about the command is doing that please?

zxandris

  • Full Member
  • ***
  • Posts: 170
Re: Formatting a file size to something more human readable
« Reply #7 on: October 16, 2023, 01:18:54 pm »
Like on windows it shows say 1.4mb I wanted to get that, or close to it.  Obviously as accurate to the file size as I could.
IMHO the:
Code: Pascal  [Select][+][-]
  1. md := size mod MB;
will result to value in range 0..1048575, not 0..99. Same for others.

I sort of get that which is what my little bit of string manipulation was doing, at least so I thought.  Taking the first two digits of the mod of the divide to then get the "." aspect.  But I think @GetMem's function does this better, though I'm not actually sure what the formatting string actually does yet :)

Bart

  • Hero Member
  • *****
  • Posts: 5649
    • Bart en Mariska's Webstek
Re: Formatting a file size to something more human readable
« Reply #8 on: October 16, 2023, 03:26:22 pm »
From one of my units, using simple format() instead of formatfloat:

Code: Pascal  [Select][+][-]
  1. const
  2.   Kilo = 1024;
  3.   Mega = Kilo * Kilo;
  4.   Giga = UInt64(Mega) * Kilo;
  5.   Tera = UInt64(Giga) * Kilo;
  6.   SBytes = 'bytes';
  7.   SKilo = 'Kb';
  8.   SMega = 'Mb';
  9.   SGiga = 'Gb';
  10.   STera = 'Tb';
  11.  
  12. function TBackupForm.BytesToStr(const Bytes: UInt64): String;
  13. var S: String;
  14. begin
  15.   if (Bytes > Tera) then
  16.   begin
  17.     S := Format('%.2f %s',[Bytes/Tera, STera]);
  18.   end
  19.   else if (Bytes > Giga) then
  20.   begin
  21.     S := Format('%.2f %s',[Bytes/Giga, SGiga]);
  22.   end
  23.   else if (Bytes > Mega) then
  24.   begin
  25.     S := Format('%.2f %s',[Bytes/Mega, SMega]);
  26.   end
  27.   else if (Bytes > Kilo) then
  28.   begin
  29.     S := Format('%.2f %s',[Bytes/Kilo, SKilo]);
  30.   end
  31.   else
  32.   begin
  33.     S := Format('%d %s',[Bytes, SBytes]);
  34.   end;
  35.   Result := S;
  36. end;

alpine

  • Hero Member
  • *****
  • Posts: 1410
Re: Formatting a file size to something more human readable
« Reply #9 on: October 16, 2023, 03:29:02 pm »
Like on windows it shows say 1.4mb I wanted to get that, or close to it.  Obviously as accurate to the file size as I could.
IMHO the:
Code: Pascal  [Select][+][-]
  1. md := size mod MB;
will result to value in range 0..1048575, not 0..99. Same for others.

I sort of get that which is what my little bit of string manipulation was doing, at least so I thought.  Taking the first two digits of the mod of the divide to then get the "." aspect.  But I think @GetMem's function does this better, though I'm not actually sure what the formatting string actually does yet :)
The GetMem function does it right, not better.
To correct your math, the md should be normalized to the amount of the divisor (KB,MB,GB) before taking the digits.

P.S. Just try both functions with the value 1048576+1048575
« Last Edit: October 16, 2023, 03:46:36 pm by alpine »
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

wp

  • Hero Member
  • *****
  • Posts: 13275
Re: Formatting a file size to something more human readable
« Reply #10 on: October 16, 2023, 04:09:54 pm »
I will never get used to this, but strictly speaking 1 kBytes is 1000 bytes - "kilo" is the abbreviation of 1000. If you mean 1024 bit the correct unit is "kiB" (1kiB = 1024 bytes). Similarly MeB, GiB, TeB, ... See https://www.techtarget.com/searchstorage/definition/mebibyte-MiB

440bx

  • Hero Member
  • *****
  • Posts: 5913
Re: Formatting a file size to something more human readable
« Reply #11 on: October 16, 2023, 06:38:07 pm »
I will never get used to this, but strictly speaking 1 kBytes is 1000 bytes - "kilo" is the abbreviation of 1000.
I am in that "never get used to that" boat too particularly considering that Windows (and possibly other OSs) report sizes using 1024 as the block size while marketers, in self serving fashion, market the size of their wares (particularly hard drives) using 1000 as the block size.

FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

alpine

  • Hero Member
  • *****
  • Posts: 1410
Re: Formatting a file size to something more human readable
« Reply #12 on: October 16, 2023, 07:03:14 pm »
However, since the OP has defined the base of the numeral system as 2^10 (not 10^3), he must perform the correct operations to achieve the correct result.
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

zxandris

  • Full Member
  • ***
  • Posts: 170
Re: Formatting a file size to something more human readable
« Reply #13 on: October 17, 2023, 05:25:11 pm »
From one of my units, using simple format() instead of formatfloat:

Code: Pascal  [Select][+][-]
  1. const
  2.   Kilo = 1024;
  3.   Mega = Kilo * Kilo;
  4.   Giga = UInt64(Mega) * Kilo;
  5.   Tera = UInt64(Giga) * Kilo;
  6.   SBytes = 'bytes';
  7.   SKilo = 'Kb';
  8.   SMega = 'Mb';
  9.   SGiga = 'Gb';
  10.   STera = 'Tb';
  11.  
  12. function TBackupForm.BytesToStr(const Bytes: UInt64): String;
  13. var S: String;
  14. begin
  15.   if (Bytes > Tera) then
  16.   begin
  17.     S := Format('%.2f %s',[Bytes/Tera, STera]);
  18.   end
  19.   else if (Bytes > Giga) then
  20.   begin
  21.     S := Format('%.2f %s',[Bytes/Giga, SGiga]);
  22.   end
  23.   else if (Bytes > Mega) then
  24.   begin
  25.     S := Format('%.2f %s',[Bytes/Mega, SMega]);
  26.   end
  27.   else if (Bytes > Kilo) then
  28.   begin
  29.     S := Format('%.2f %s',[Bytes/Kilo, SKilo]);
  30.   end
  31.   else
  32.   begin
  33.     S := Format('%d %s',[Bytes, SBytes]);
  34.   end;
  35.   Result := S;
  36. end;

This seems to be what I'm looking for, thank you very much.  I have noticed that in win 10 it appears to round up the size to the upper whole number but I'm happier with the more accurate reading from this.  Thank you, thank you all actually it's been interesting to see the discussion on this.  Thanks

Kays

  • Hero Member
  • *****
  • Posts: 627
  • Whasup!?
    • KaiBurghardt.de
Re: Formatting a file size to something more human-readable
« Reply #14 on: October 17, 2023, 10:20:28 pm »
What is intended to be after the decimal dot in the result of your function?
Well, 0.5 KiB should mean 512 bytes. It would be very confusing if 0.5 KiB meant 0 KiB + 5 bytes.
[…] The GetMem function does it right, not better. […]
In a more elegant fashion, the task is just engineering notation to a base of 1024. For that you can regard the problem as converting a value to a base 1024 number. Horner’s method is the textbook method for obtaining the “digit” values.
Code: Pascal  [Select][+][-]
  1.         {$longStrings on}
  2.         {$modeSwitch result+}
  3.         function formatSize(size: valReal): string;
  4.                 { Assuming `maxInt` ≤ 2⁶⁴, the largest binary prefix is Ei. }
  5.                 const
  6.                         base = 1024;
  7.                         maxOrder = 6;
  8.                 type
  9.                         order = 0..maxOrder;
  10.                 var
  11.                         sizeUnit: order;
  12.                         prefixedUnit: array[order] of string;
  13.                 begin
  14.                         sizeUnit := 0;
  15.                         prefixedUnit[0] := 'B';
  16.                         prefixedUnit[1] := 'KiB';  prefixedUnit[2] := 'MiB';
  17.                         prefixedUnit[3] := 'GiB';  prefixedUnit[4] := 'TiB';
  18.                         prefixedUnit[5] := 'PiB';  prefixedUnit[6] := 'EiB';
  19.                         { NB: Input checking (`size` < 0) has been omitted. }
  20.                         while (size >= base) and (sizeUnit < maxOrder) do
  21.                         begin
  22.                                 size := size / base;
  23.                                 sizeUnit := sizeUnit + 1;
  24.                         end;
  25.                         writeStr(result, size:3:1, prefixedUnit[sizeUnit]);
  26.                 end;
Just as a starter. Note this will emit .0 for values ≤ 1023 bytes.
Yours Sincerely
Kai Burghardt

 

TinyPortal © 2005-2018