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

681

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

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

我已经厌烦了每个项目都要写这个实用程序方法。在Apache Commons中有没有静态方法可以完成这个任务?


42
如果您使用标准单位,1024应该变成“1KiB”,而1024*1024则应该变成“1MiB”。参见http://en.wikipedia.org/wiki/Binary_prefix。 - Pascal Cuoq
4
@ Pascal Cuoq:谢谢提供参考。在阅读这篇文章之前,我不知道在欧盟我们必须按法律要求使用正确的前缀。请允许我翻译为:“感谢您提供的参考。我直到阅读它之前才意识到,在欧盟,按法律要求我们必须使用正确的前缀。” - JeremyP
2
@DerMike,你曾经提到过“直到这样的库存在”。现在它已经成为现实了。:-)https://dev59.com/XG865IYBdhLWcg3wi_Q2#38390338 - Christian Esken
1
@AaronDigulla 你说得对。为什么那个比这个问题早两个月的问题被关闭为重复,而不是这个呢? - hc_dev
1
@hc_dev 我想早两个月提出的那个问题被关闭是因为这个问题有更好的答案。这两个问题都是在2010年发布的,另一个问题一直到2013年才关闭。(现在想想,SO真应该有一个“合并问题”的功能,把两个问题的答案合并到一个地方。) - FeRD
显示剩余3条评论
31个回答

7

org.springframework.util.unit.DataSize可以至少用于计算满足此要求。然后一个简单的装饰器就可以搞定。


我的需求是打印系统的内存,这对我很有帮助,因为我知道它总是需要以MB为单位打印。 - Pavan Kumar

5
    public static String floatForm (double d)
    {
       return new DecimalFormat("#.##").format(d);
    }


    public static String bytesToHuman (long size)
    {
        long Kb = 1  * 1024;
        long Mb = Kb * 1024;
        long Gb = Mb * 1024;
        long Tb = Gb * 1024;
        long Pb = Tb * 1024;
        long Eb = Pb * 1024;

        if (size <  Kb)                 return floatForm(        size     ) + " byte";
        if (size >= Kb && size < Mb)    return floatForm((double)size / Kb) + " Kb";
        if (size >= Mb && size < Gb)    return floatForm((double)size / Mb) + " Mb";
        if (size >= Gb && size < Tb)    return floatForm((double)size / Gb) + " Gb";
        if (size >= Tb && size < Pb)    return floatForm((double)size / Tb) + " Tb";
        if (size >= Pb && size < Eb)    return floatForm((double)size / Pb) + " Pb";
        if (size >= Eb)                 return floatForm((double)size / Eb) + " Eb";

        return "???";
    }

4
现在有一个包含单位格式化的库可用。我将其添加到 triava库中,因为唯一存在的其他库似乎是Android的一个库。
它可以以任意精度格式化数字,在3种不同的系统(SI、IEC、JEDEC)和各种输出选项中。以下是 triava单元测试中的一些代码示例:
UnitFormatter.formatAsUnit(1126, UnitSystem.SI, "B");
// = "1.13kB"
UnitFormatter.formatAsUnit(2094, UnitSystem.IEC, "B");
// = "2.04KiB"

打印精确的千、兆值(这里以W为单位):

UnitFormatter.formatAsUnits(12_000_678, UnitSystem.SI, "W", ", ");
// = "12MW, 678W"

您可以传递一个DecimalFormat来自定义输出:

UnitFormatter.formatAsUnit(2085, UnitSystem.IEC, "B", new DecimalFormat("0.0000"));
// = "2.0361KiB"

任意对千或兆值进行操作时,可以将它们拆分为组件:
UnitComponent uc = new  UnitComponent(123_345_567_789L, UnitSystem.SI);
int kilos = uc.kilo(); // 567
int gigas = uc.giga(); // 123

4

另一个简洁的解决方案,不需要循环,但具有区域设置敏感的格式和正确的二进制前缀:

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

3
创建一个接口:
public interface IUnits {
    public String format(long size, String pattern);
    public long getUnitSize();
}

创建StorageUnits类:
import java.text.DecimalFormat;

public class StorageUnits {

    private static final long K = 1024;
    private static final long M = K * K;
    private static final long G = M * K;
    private static final long T = G * K;

    enum Unit implements IUnits {

        TERA_BYTE {
            @Override
            public String format(long size, String pattern) {
                return format(size, getUnitSize(), "TB", pattern);
            }
            @Override
            public long getUnitSize() {
                return T;
            }
            @Override
            public String toString() {
                return "Terabytes";
            }
        },
        GIGA_BYTE {
            @Override
            public String format(long size, String pattern) {
                return format(size, getUnitSize(), "GB", pattern);
            }
            @Override
            public long getUnitSize() {
                return G;
            }
            @Override
            public String toString() {
                return "Gigabytes";
            }
        },
        MEGA_BYTE {
            @Override
            public String format(long size, String pattern) {
                return format(size, getUnitSize(), "MB", pattern);
            }
            @Override
            public long getUnitSize() {
                return M;
            }
            @Override
            public String toString() {
                return "Megabytes";
            }
        },
        KILO_BYTE {
            @Override
            public String format(long size, String pattern) {
                return format(size, getUnitSize(), "kB", pattern);
            }
            @Override
            public long getUnitSize() {
                return K;
            }
            @Override
            public String toString() {
                return "Kilobytes";
            }

        };

        String format(long size, long base, String unit, String pattern) {
            return new DecimalFormat(pattern).format(
                           Long.valueOf(size).doubleValue() /
                           Long.valueOf(base).doubleValue()
            ) + unit;
        }
    }

    public static String format(long size, String pattern) {
        for(Unit unit : Unit.values()) {
            if(size >= unit.getUnitSize()) {
                return unit.format(size, pattern);
            }
        }
        return ("???(" + size + ")???");
    }

    public static String format(long size) {
        return format(size, "#,##0.#");
    }
}

称之为:

class Main {
    public static void main(String... args) {
        System.out.println(StorageUnits.format(21885));
        System.out.println(StorageUnits.format(2188121545L));
    }
}

输出:

21.4kB
2GB

3

喜欢Kotlin的人可以使用这个扩展:

fun Long.readableFormat(): String {
    if (this <= 0 ) return "0"
    val units = arrayOf("B", "kB", "MB", "GB", "TB")
    val digitGroups = (log10(this.toDouble()) / log10(1024.0)).toInt()
    return DecimalFormat("#,##0.#").format(this / 1024.0.pow(digitGroups.toDouble())).toString() + " " + units[digitGroups]
}

现在使用:
val size : Long = 90836457
val readbleString = size.readableFormat()

另一种方法
val Long.formatSize : String
    get() {
        if (this <= 0) return "0"
        val units = arrayOf("B", "kB", "MB", "GB", "TB")
        val digitGroups = (log10(this.toDouble()) / log10(1024.0)).toInt()
        return DecimalFormat("#,##0.#").format(this / 1024.0.pow(digitGroups.toDouble())).toString() + " " + units[digitGroups]
    }

现在使用
val size : Long = 90836457
val readbleString = size.formatSize

2
这是一个Go版本。为了简单起见,我只包含了二进制输出的情况。
func sizeOf(bytes int64) string {
    const unit = 1024
    if bytes < unit {
        return fmt.Sprintf("%d B", bytes)
    }

    fb := float64(bytes)
    exp := int(math.Log(fb) / math.Log(unit))
    pre := "KMGTPE"[exp-1]
    div := math.Pow(unit, float64(exp))
    return fmt.Sprintf("%.1f %ciB", fb / div, pre)
}

2
您可以使用 StringUtilsTraditionalBinarPrefix
public static String humanReadableInt(long number) {
    return TraditionalBinaryPrefix.long2String(number, ””, 1);
}

2
我使用的方法与被接受的答案略有不同:

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

1
String[] fileSizeUnits = {"bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"};

public String calculateProperFileSize(double bytes){
    String sizeToReturn = "";
    int index = 0;
    for(index = 0; index < fileSizeUnits.length; index++){
        if(bytes < 1024){
            break;
        }
        bytes = bytes / 1024;
    }

    System.out.println("File size in proper format: " + bytes + " " + fileSizeUnits[index]);
    sizeToReturn = String.valueOf(bytes) + " " + fileSizeUnits[index];
    return sizeToReturn;
}

只需添加更多的文件单元(如果有缺失),您将看到该单元的单位大小(如果您的文件具有那么长的长度):


1
为什么不是一个代码块?乍一看,似乎缺少了一个“}`”。 - Peter Mortensen
@PeterMortensen,谢谢您告诉我!那是个笔误,我已经纠正了。 - Vishwajit R. Shinde

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接