首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Delphi格式化字节到GB

Delphi格式化字节到GB
EN

Stack Overflow用户
提问于 2014-07-26 10:30:07
回答 2查看 3.3K关注 0票数 4

我使用以下函数将Bytes格式化为更具人类可读性的格式,但它正在返回不正确的信息。

代码语言:javascript
复制
 //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;

示例:

代码语言:javascript
复制
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 )

有人能解释为什么它返回不正确的信息,更重要的是知道如何修复它从而返回正确的信息吗?

EN

回答 2

Stack Overflow用户

发布于 2015-05-02 05:53:40

问题是算术溢出。您可以如下所示的函数:

代码语言:javascript
复制
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;
票数 7
EN

Stack Overflow用户

发布于 2014-07-26 12:35:19

正如注释中所说的,您的问题是您的32位整数被64位值溢出,因此它被截断为32位(前32位被丢弃,所以f.ex )。5GB的值将被理解为1GB)。而且,由于您正在谈论的是大小,所以您确实不应该使用整数,因为这样您就会将一半的范围丢弃在在任何情况下都不能有效的值上(文件f.ex.不能有-2048字节的大小)。

我已经使用了以下两个函数一段时间了。没有小数参数的一个将返回最多3个小数,但只有在必要的情况下(即。如果大小正好是1GB,那么它将返回字符串“1GB”而不是“1000 Gb”(如果您的小数点是逗号)。

带有小数参数的那个总是返回带有这个小数的值。

还请注意,计算是使用二进制标度(1KB= 1024字节)完成的。如果希望将其更改为十进制,则应该用1000更改1024值,并可能更改SizeUnits数组。

代码语言:javascript
复制
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 (您可能不会使用大于8EB= apprx的acros文件。8.000.000 TeraBytes在您的生命周期:-),所以Int64在这种情况下应该足够了)。

另外,CharInSet函数是来自Delphi版本的函数。可以将其定义为:

代码语言:javascript
复制
TYPE TCharacterSet = SET OF CHAR;

FUNCTION CharInSet(C : CHAR ; CONST Z : TCharacterSet) : BOOLEAN; INLINE;
  BEGIN
    Result:=(C IN Z)
  END;

或者直接在源代码中替换,如果您使用的是Delphi的前Unicode版本.

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24970073

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档