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?
See the following functions, all in the shlwapi library.
StrFormatByteSizeA
(DWord parameter)StrFormatByteSizeW
(Int64 parameter)StrFormatByteSize64
(In Unicode mode, it's reallyStrFormatByteSizeW
)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;
Not exactly what you're after but I have a function in my library that may give you an idea how to tackle this one:
function FormatByteSize(const bytes: Longword): string;
var
B: byte;
KB: word;
MB: Longword;
GB: Longword;
TB: UInt64;
begin
B := 1; //byte
KB := 1024 * B; //kilobyte
MB := 1000 * KB; //megabyte
GB := 1000 * MB; //gigabyte
TB := 1000 * GB; //teraabyte
if bytes > TB then
result := FormatFloat('#.## TB', bytes / TB)
else
if bytes > GB then
result := FormatFloat('#.## GB', bytes / GB)
else
if bytes > MB then
result := FormatFloat('#.## MB', bytes / MB)
else
if bytes > KB then
result := FormatFloat('#.## KB', bytes / KB)
else
result := FormatFloat('#.## bytes', bytes) ;
end;
This is from my dzlib unit u_dzConvertUtils:
/// these contants refer to the "Xx binary byte" units as defined by the
/// International Electronical Commission (IEC) and endorsed by the
/// IEE and CiPM </summary>
const
OneKibiByte = Int64(1024);
OneMebiByte = Int64(1024) * OneKibiByte;
OneGibiByte = Int64(1024) * OneMebiByte;
OneTebiByte = Int64(1024) * OneGibiByte;
OnePebiByte = Int64(1024) * OneTebiByte;
OneExbiByte = Int64(1024) * OnePebiByte;
/// <summary>
/// Converts a file size to a human readable string, e.g. 536870912000 = 5.00 GiB (gibibyte) </summary>
function FileSizeToHumanReadableString(_FileSize: Int64): string;
begin
if _FileSize > 5 * OneExbiByte then
Result := Format(_('%.2f EiB'), [_FileSize / OneExbiByte])
else if _FileSize > 5 * OnePebiByte then
Result := Format(_('%.2f PiB'), [_FileSize / OnePebiByte])
else if _FileSize > 5 * OneTebiByte then
Result := Format(_('%.2f TiB'), [_FileSize / OneTebiByte])
else if _FileSize > 5 * OneGibiByte then
Result := Format(_('%.2f GiB'), [_FileSize / OneGibiByte])
else if _FileSize > 5 * OneMebiByte then
Result := Format(_('%.2f MiB'), [_FileSize / OneMebiByte])
else if _FileSize > 5 * OneKibiByte then
Result := Format(_('%.2f KiB'), [_FileSize / OneKibiByte])
else
Result := Format(_('%d Bytes'), [_FileSize]);
end;
来源:https://stackoverflow.com/questions/1285979/delphi-function-to-display-number-of-bytes-as-windows-does