我使用以下函数将Bytes格式化为更易于阅读的格式,但它返回的信息不正确。
//Format file byte size
function FormatByteSize(const bytes: LongInt): string;
const
B = 1; //byte
KB = 1024 * B; //kilobyte
MB = 1024 * KB; //megabyte
GB = 1024 * MB; //gigabyte
begin
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;
示例:
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(FormatByteSize(323889675684)); //Returns 1.65GB when it should be ~301GB
end;
参考文献:http://delphi.about.com/od/delphitips2008/qt/format-bytes.htm(作者:Zarco Gajic)
任何人都可以解释为什么它会返回不正确的信息,更重要的是知道如何修复它以便它返回正确的信息吗?
答案 0 :(得分:6)
问题是算术溢出。你可以像这样重写这个函数:
uses
Math;
function ConvertBytes(Bytes: Int64): string;
const
Description: Array [0 .. 8] of string = ('Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');
var
i: Integer;
begin
i := 0;
while Bytes > Power(1024, i + 1) do
Inc(i);
Result := FormatFloat('###0.##', Bytes / Power(1024, i)) + #32 + Description[i];
end;
答案 1 :(得分:1)
就像在评论中所说的那样,你的问题是你的32位整数溢出64位,因此它会被截断为32位(前32位被简单地丢弃,所以f.ex. 5 Gb的值将被理解为1 Gb)。另外,既然你在谈论大小,你真的不应该使用整数,因为你会丢弃一半的范围,而这些值在任何情况下都是无效的(文件,f.ex。 ,不能有-2048字节的大小。
我有一段时间使用了以下两个功能。没有Decimals参数的那个将返回最多3个小数,但仅在必要时(即,如果大小正好是1 Gb,那么它将返回字符串" 1 Gb"而不是" 1,000 Gb"(如果您的小数点是逗号))。
具有Decimals参数的参数将始终返回带有该小数位数的值。
另请注意,计算是使用二进制标度(1 Kb = 1024字节)完成的。如果您希望将其更改为十进制刻度,则应将1024值更改为1000,并且可能也更改为SizeUnits数组。
CONST
SizeUnits : ARRAY[0..8] OF PChar = ('bytes','Kb','Mb','Gb','Tb','Pb','Eb','Zb','Yb');
FUNCTION SizeStr(Size : UInt64) : String; OVERLOAD;
VAR
P : Integer;
BEGIN
Result:=SizeStr(Size,3);
IF Size>=1024 THEN BEGIN
P:=PRED(LastDelimiter(' ',Result));
WHILE COPY(Result,P,1)='0' DO BEGIN
DELETE(Result,P,1);
DEC(P)
END;
IF CharInSet(Result[P],['.',',']) THEN DELETE(Result,P,1)
END
END;
FUNCTION SizeStr(Size : UInt64 ; Decimals : BYTE) : String; OVERLOAD;
VAR
I : Cardinal;
S : Extended;
BEGIN
S:=Size;
FOR I:=LOW(SizeUnits) TO HIGH(SizeUnits) DO BEGIN
IF S<1024.0 THEN BEGIN
IF I=LOW(SizeUnits) THEN Decimals:=0;
Result:=Format('%.'+IntToStr(Decimals)+'f',[S]);
Result:=Result+' '+StrPas(SizeUnits[I]);
EXIT
END;
S:=S/1024.0
END
END;
如果您使用的是不具有UInt64类型的Delphi编译器版本,则可以使用Int64(您可能无法获得大于8 Eb = apprx的acros文件.8.000.000 TeraBytes在你的一生中:-),所以在这种情况下Int64就足够了。)
此外,CharInSet函数是来自Unicode版本的Delphi中的函数。它可以实现为:
TYPE TCharacterSet = SET OF CHAR;
FUNCTION CharInSet(C : CHAR ; CONST Z : TCharacterSet) : BOOLEAN; INLINE;
BEGIN
Result:=(C IN Z)
END;
或直接在源代码中替换,如果您使用的是Unicode前版本的Delphi。