Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi Function to Display Number of Bytes as Windows Does

This is a simple one (I think).

Is there a system built in function, or a function that someone has created that can be called from Delphi, that will display a number of bytes (e.g. a filesize), the way Windows displays in a file's Properties box?

e.g. This is how Windows property box displays various sizes:

539 bytes (539 bytes)
35.1 KB (35,974 bytes)
317 MB (332,531,365 bytes)
2.07 GB (2,224,617,077 bytes)

The display is smart about using bytes, KB, MB or GB, and shows only 3 significant digits for the KB, MB and GB. It then follows that by displaying the exact number of bytes in parenthesis with commas separating the thousands. It is a very nice display, well thought out.

Does anyone know of such a function?


Edit: I'm very surprised there wasn't a function for this.

Thanks for your helpful ideas. I've come up with this, which seems to work:

function BytesToDisplay(A:int64): string;
var
  A1, A2, A3: double;
begin
  A1 := A / 1024;
  A2 := A1 / 1024;
  A3 := A2 / 1024;
  if A1 < 1 then Result := floattostrf(A, ffNumber, 15, 0) + ' bytes'
  else if A1 < 10 then Result := floattostrf(A1, ffNumber, 15, 2) + ' KB'
  else if A1 < 100 then Result := floattostrf(A1, ffNumber, 15, 1) + ' KB'
  else if A2 < 1 then Result := floattostrf(A1, ffNumber, 15, 0) + ' KB'
  else if A2 < 10 then Result := floattostrf(A2, ffNumber, 15, 2) + ' MB'
  else if A2 < 100 then Result := floattostrf(A2, ffNumber, 15, 1) + ' MB'
  else if A3 < 1 then Result := floattostrf(A2, ffNumber, 15, 0) + ' MB'
  else if A3 < 10 then Result := floattostrf(A3, ffNumber, 15, 2) + ' GB'
  else if A3 < 100 then Result := floattostrf(A3, ffNumber, 15, 1) + ' GB'
  else Result := floattostrf(A3, ffNumber, 15, 0) + ' GB';
  Result := Result + ' (' + floattostrf(A, ffNumber, 15, 0) + ' bytes)';
end;

This is probably good enough, but is there anything better?

like image 378
lkessler Avatar asked Aug 17 '09 02:08

lkessler


1 Answers

See the following functions, all in the shlwapi library.

  • StrFormatByteSizeA (DWord parameter)
  • StrFormatByteSizeW (Int64 parameter)
  • StrFormatByteSize64 (In Unicode mode, it's really StrFormatByteSizeW)
  • StrFormatByteSizeEx (Vista SP2; can control rounding)

Any of them will give you the first portion of your desired display format. Check the documentation or write your own tests to confirm that they give the conversions you expect regarding whether a megabyte consists of 1000 or 1024 kilobytes. For the second part of your display format, you can start with the answers to another Stack Overflow question:

  • How to convert int to currency? (He was really asking how to insert commas, not specifically about money.)

But perhaps that question is the wrong avenue to go down since all the suggestions there, as well as FloatToStrF, fail at the upper limits of Int64. You'll lose a few bytes, but I consider those pretty important bytes since the purpose of the second value in that display format is to provide an exact number.

Once you have all the pieces, glue them together. I'm using a hypothetical IntToStrCommas function here, and if you want to implement that as FloatToStrF, go ahead.

function BytesToDisplay(const num: Int64): string;
var
  // If GB is the largest unit available, then 20 characters is
  // enough for "17,179,869,183.99 GB", which is MaxUInt64.
  buf: array[0..20] of Char;
begin
  if StrFormatByteSize64(num, buf, Length(buf)) = nil then
    raise EConvertError.CreateFmt('Error converting %d', [num]);
  Result := Format('%s (%s bytes)', [buf, IntToStrCommas(num)]);
end;
like image 102
Rob Kennedy Avatar answered Sep 26 '22 14:09

Rob Kennedy