如何在Java中将字节大小转换为人类可读的格式?

比如1024应该变成“1 Kb”,1024*1024应该变成“1 Mb”。

我有点厌倦了为每个项目写这个实用方法。在Apache Commons中有这样的静态方法吗?


当前回答

下面是上面Java正确的共识答案的c# .NET等价版本 (下面还有一个代码更短的例子):

    public static String BytesNumberToHumanReadableString(long bytes, bool SI1000orBinary1024)
    {
        int unit = SI1000orBinary1024 ? 1000 : 1024;
        if (bytes < unit)
            return bytes + " B";

        int exp = (int)(Math.Log(bytes) / Math.Log(unit));
        String pre = (SI1000orBinary1024 ? "kMGTPE" : "KMGTPE")[(exp - 1)] + (SI1000orBinary1024 ? "" : "i");
        return String.Format("{0:F1} {1}B", bytes / Math.Pow(unit, exp), pre);
    }

从技术上讲,如果我们坚持使用国际单位制,这个程序适用于任何常规的数字使用。专家们还给出了许多不错的答案。假设您正在对gridview上的数字进行数据绑定,有必要从它们中查看性能优化例程。

PS:这个帖子是因为当我在做一个c#项目时,这个问题/答案出现在谷歌搜索的顶部。

其他回答

我使用了一个比公认答案稍作修改的方法:

public static String formatFileSize(long bytes) {
    if (bytes <= 0)
        return "";
    if (bytes < 1000)
        return bytes + " B";

    CharacterIterator ci = new StringCharacterIterator("kMGTPE");
    while (bytes >= 99_999) {
        bytes /= 1000;
        ci.next();
    }
    return String.format(Locale.getDefault(), "%.1f %cB", bytes / 1000.0, ci.current());
}

因为我想看到另一个输出:

                              SI

                   0:            <--------- instead of 0 B
                  27:       27 B
                 999:      999 B
                1000:     1.0 kB
                1023:     1.0 kB
                1024:     1.0 kB
                1728:     1.7 kB
              110592:     0.1 MB <--------- instead of 110.6 kB
             7077888:     7.1 MB
           452984832:     0.5 GB <--------- instead of 453.0 MB
         28991029248:    29.0 GB

我通常是这样做的:

public static String getFileSize(double size) {
    return _getFileSize(size,0,1024);
}

public static String _getFileSize(double size, int i, double base) {
    String units = " KMGTP";
    String unit = (i>0)?(""+units.charAt(i)).toUpperCase()+"i":"";
    if(size<base)
        return size +" "+unit.trim()+"B";
    else {
        size = Math.floor(size/base);
        return _getFileSize(size,++i,base);
    }
}
public String humanReadable(long size) {
    long limit = 10 * 1024;
    long limit2 = limit * 2 - 1;
    String negative = "";
    if(size < 0) {
        negative = "-";
        size = Math.abs(size);
    }

    if(size < limit) {
        return String.format("%s%s bytes", negative, size);
    } else {
        size = Math.round((double) size / 1024);
        if (size < limit2) {
            return String.format("%s%s kB", negative, size);
        } else {
            size = Math.round((double)size / 1024);
            if (size < limit2) {
                return String.format("%s%s MB", negative, size);
            } else {
                size = Math.round((double)size / 1024);
                if (size < limit2) {
                    return String.format("%s%s GB", negative, size);
                } else {
                    size = Math.round((double)size / 1024);
                        return String.format("%s%s TB", negative, size);
                }
            }
        }
    }
}

这是另一个简洁的解决方案,没有循环,但具有区域敏感格式和正确的二进制前缀:

import java.util.Locale;

public final class Bytes {

  private Bytes() {
  }

  public static String format(long value, Locale locale) {
    if (value < 1024) {
      return value + " B";
    }
    int z = (63 - Long.numberOfLeadingZeros(value)) / 10;
    return String.format(locale, "%.1f %siB", (double) value / (1L << (z * 10)), " KMGTPE".charAt(z));
  }
}

测试:

Locale locale = Locale.getDefault()
System.out.println(Bytes.format(1L, locale))
System.out.println(Bytes.format(2L * 1024, locale))
System.out.println(Bytes.format(3L * 1024 * 1024, locale))
System.out.println(Bytes.format(4L * 1024 * 1024 * 1024, locale))
System.out.println(Bytes.format(5L * 1024 * 1024 * 1024 * 1024, locale))
System.out.println(Bytes.format(6L * 1024 * 1024 * 1024 * 1024 * 1024, locale))
System.out.println(Bytes.format(Long.MAX_VALUE, locale))

输出:

1 B
2.0 KiB
3.0 MiB
4.0 GiB
5.0 GiB
6.0 PiB
8.0 EiB

字节单位允许你这样做:

long input1 = 1024;
long input2 = 1024 * 1024;

Assert.assertEquals("1 KiB", BinaryByteUnit.format(input1));
Assert.assertEquals("1 MiB", BinaryByteUnit.format(input2));

Assert.assertEquals("1.024 KB", DecimalByteUnit.format(input1, "#.0"));
Assert.assertEquals("1.049 MB", DecimalByteUnit.format(input2, "#.000"));

NumberFormat format = new DecimalFormat("#.#");
Assert.assertEquals("1 KiB", BinaryByteUnit.format(input1, format));
Assert.assertEquals("1 MiB", BinaryByteUnit.format(input2, format));

我写了另一个叫做storage-units的库,它允许你这样做:

String formattedUnit1 = StorageUnits.formatAsCommonUnit(input1, "#");
String formattedUnit2 = StorageUnits.formatAsCommonUnit(input2, "#");
String formattedUnit3 = StorageUnits.formatAsBinaryUnit(input1);
String formattedUnit4 = StorageUnits.formatAsBinaryUnit(input2);
String formattedUnit5 = StorageUnits.formatAsDecimalUnit(input1, "#.00", Locale.GERMAN);
String formattedUnit6 = StorageUnits.formatAsDecimalUnit(input2, "#.00", Locale.GERMAN);
String formattedUnit7 = StorageUnits.formatAsBinaryUnit(input1, format);
String formattedUnit8 = StorageUnits.formatAsBinaryUnit(input2, format);

Assert.assertEquals("1 kB", formattedUnit1);
Assert.assertEquals("1 MB", formattedUnit2);
Assert.assertEquals("1.00 KiB", formattedUnit3);
Assert.assertEquals("1.00 MiB", formattedUnit4);
Assert.assertEquals("1,02 kB", formattedUnit5);
Assert.assertEquals("1,05 MB", formattedUnit6);
Assert.assertEquals("1 KiB", formattedUnit7);
Assert.assertEquals("1 MiB", formattedUnit8);

如果你想强制某个单位,可以这样做:

String formattedUnit9 = StorageUnits.formatAsKibibyte(input2);
String formattedUnit10 = StorageUnits.formatAsCommonMegabyte(input2);

Assert.assertEquals("1024.00 KiB", formattedUnit9);
Assert.assertEquals("1.00 MB", formattedUnit10);