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

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

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


当前回答

下面是从aioobe转换到Kotlin的转换:

/**
 * https://stackoverflow.com/a/3758880/1006741
 */
fun Long.humanReadableByteCountBinary(): String {
    val b = when (this) {
        Long.MIN_VALUE -> Long.MAX_VALUE
        else -> abs(this)
    }
    return when {
        b < 1024L -> "$this B"
        b <= 0xfffccccccccccccL shr 40 -> "%.1f KiB".format(Locale.UK, this / 1024.0)
        b <= 0xfffccccccccccccL shr 30 -> "%.1f MiB".format(Locale.UK, this / 1048576.0)
        b <= 0xfffccccccccccccL shr 20 -> "%.1f GiB".format(Locale.UK, this / 1.073741824E9)
        b <= 0xfffccccccccccccL shr 10 -> "%.1f TiB".format(Locale.UK, this / 1.099511627776E12)
        b <= 0xfffccccccccccccL -> "%.1f PiB".format(Locale.UK, (this shr 10) / 1.099511627776E12)
        else -> "%.1f EiB".format(Locale.UK, (this shr 20) / 1.099511627776E12)
    }
}

其他回答

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

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

下面是从aioobe转换到Kotlin的转换:

/**
 * https://stackoverflow.com/a/3758880/1006741
 */
fun Long.humanReadableByteCountBinary(): String {
    val b = when (this) {
        Long.MIN_VALUE -> Long.MAX_VALUE
        else -> abs(this)
    }
    return when {
        b < 1024L -> "$this B"
        b <= 0xfffccccccccccccL shr 40 -> "%.1f KiB".format(Locale.UK, this / 1024.0)
        b <= 0xfffccccccccccccL shr 30 -> "%.1f MiB".format(Locale.UK, this / 1048576.0)
        b <= 0xfffccccccccccccL shr 20 -> "%.1f GiB".format(Locale.UK, this / 1.073741824E9)
        b <= 0xfffccccccccccccL shr 10 -> "%.1f TiB".format(Locale.UK, this / 1.099511627776E12)
        b <= 0xfffccccccccccccL -> "%.1f PiB".format(Locale.UK, (this shr 10) / 1.099511627776E12)
        else -> "%.1f EiB".format(Locale.UK, (this shr 20) / 1.099511627776E12)
    }
}

有趣的事实:这里发布的原始代码片段是Stack Overflow上被复制最多的Java代码片段,它是有缺陷的。它被修好了,但却变得一团糟。 本文的完整故事:有史以来复制最多的堆栈溢出代码片段是有缺陷的!

来源:格式化字节大小到人类可读的格式|编程。指南

SI(1 k = 1,000)

public static String humanReadableByteCountSI(long bytes) {
    if (-1000 < bytes && bytes < 1000) {
        return bytes + " B";
    }
    CharacterIterator ci = new StringCharacterIterator("kMGTPE");
    while (bytes <= -999_950 || bytes >= 999_950) {
        bytes /= 1000;
        ci.next();
    }
    return String.format("%.1f %cB", bytes / 1000.0, ci.current());
}

二进制(1's = 1,024)

public static String humanReadableByteCountBin(long bytes) {
    long absB = bytes == Long.MIN_VALUE ? Long.MAX_VALUE : Math.abs(bytes);
    if (absB < 1024) {
        return bytes + " B";
    }
    long value = absB;
    CharacterIterator ci = new StringCharacterIterator("KMGTPE");
    for (int i = 40; i >= 0 && absB > 0xfffccccccccccccL >> i; i -= 10) {
        value >>= 10;
        ci.next();
    }
    value *= Long.signum(bytes);
    return String.format("%.1f %ciB", value / 1024.0, ci.current());
}

示例输出:

                             SI     BINARY

                  0:        0 B        0 B
                 27:       27 B       27 B
                999:      999 B      999 B
               1000:     1.0 kB     1000 B
               1023:     1.0 kB     1023 B
               1024:     1.0 kB    1.0 KiB
               1728:     1.7 kB    1.7 KiB
             110592:   110.6 kB  108.0 KiB
            7077888:     7.1 MB    6.8 MiB
          452984832:   453.0 MB  432.0 MiB
        28991029248:    29.0 GB   27.0 GiB
      1855425871872:     1.9 TB    1.7 TiB
9223372036854775807:     9.2 EB    8.0 EiB   (Long.MAX_VALUE)

我们可以完全避免使用缓慢的Math.pow()和Math.log()方法,而不会牺牲简单性,因为单位之间的因子(例如,B, KB, MB等)是1024,即2^10。Long类有一个方便的numberofleadingzero()方法,我们可以用它来告诉大小值落在哪个单元中。

重点:大小单位的距离为10位(1024 = 2^10),这意味着最高位的位置-换句话说,前导零的数量-相差10(字节= KB*1024, KB = MB*1024,等等)。

前导零数与大小单位的相关性:

# of leading 0's Size unit
>53 B (Bytes)
>43 KB
>33 MB
>23 GB
>13 TB
>3 PB
<=3 EB

最终代码:

public static String formatSize(long v) {
    if (v < 1024) return v + " B";
    int z = (63 - Long.numberOfLeadingZeros(v)) / 10;
    return String.format("%.1f %sB", (double)v / (1L << (z*10)), " KMGTPE".charAt(z));
}
filename=filedilg.getSelectedFile().getAbsolutePath();
File file=new File(filename);

String disp=FileUtils.byteCountToDisplaySize(file.length());
System.out.println("THE FILE PATH IS "+file+"THIS File SIZE IS IN MB "+disp);