在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个回答

1
这是C# .NET中与上面Java正确的共识答案对应的代码(下面还有另一段更短的代码):
    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);
    }

从技术上讲,如果我们坚持使用国际单位制,这个例程适用于任何常规的数字使用。还有许多其他专家给出的好答案。假设您正在对网格视图上的数字进行数据绑定,那么检查来自他们的性能优化例程是值得的。
PS:这篇文章是因为在我做C#项目时,在Google搜索中排名第一的问题/答案浮现而发布的。

1

这是将aioobe的转换转换为Kotlin的转换:

/**
 * https://dev59.com/XG865IYBdhLWcg3wi_Q2#3758880
 */
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)
    }
}

1
为什么要使用浮点数?有特定的原因吗?如果支持64位整数,它不会被覆盖吗? - Peter Mortensen

1

1
也许您可以使用这段代码(使用C#编写):

long Kb = 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 size.ToString() + " byte";

if (size < Mb)  return (size / Kb).ToString("###.##") + " Kb.";
if (size < Gb)  return (size / Mb).ToString("###.##") + " Mb.";
if (size < Tb)  return (size / Gb).ToString("###.##") + " Gb.";
if (size < Pb)  return (size / Tb).ToString("###.##") + " Tb.";
if (size < Eb)  return (size / Pb).ToString("###.##") + " Pb.";
if (size >= Eb) return (size / Eb).ToString("###.##") + " Eb.";

return "invalid size";

1
这里有很多冗余。需要一个循环和一个(静态)字符串列表。 - Peter Mortensen

0

在实际应用中,MB已经足够易读。

long l = 1367343104l;
    
String s = String.format("%dm", l / 1024 / 1024);

1304米


0

试试JSR 363。它的单位扩展模块,如Unicode CLDR(在GitHub:uom-systems中)可以为您完成所有这些操作。

您可以使用每个实现中包含的MetricPrefixBinaryPrefix(类似于上面的一些示例),如果您生活和工作在印度或附近国家,则可以使用IndianPrefix(也在uom-systems的公共模块中)来使用和格式化“Crore Bytes”或“Lakh Bytes”。


0
以下是一个快速、简单且易读的代码片段,可实现此功能:
/**
 * Converts byte size to human readable strings (also declares useful constants)
 *
 * @see <a href="https://en.wikipedia.org/wiki/File_size">File size</a>
 */
@SuppressWarnings("SpellCheckingInspection")
public class HumanReadableSize {
    public static final double
            KILO = 1000L, // 1000 power 1 (10 power 3)
            KIBI = 1024L, // 1024 power 1 (2 power 10)
            MEGA = KILO * KILO, // 1000 power 2 (10 power 6)
            MEBI = KIBI * KIBI, // 1024 power 2 (2 power 20)
            GIGA = MEGA * KILO, // 1000 power 3 (10 power 9)
            GIBI = MEBI * KIBI, // 1024 power 3 (2 power 30)
            TERA = GIGA * KILO, // 1000 power 4 (10 power 12)
            TEBI = GIBI * KIBI, // 1024 power 4 (2 power 40)
            PETA = TERA * KILO, // 1000 power 5 (10 power 15)
            PEBI = TEBI * KIBI, // 1024 power 5 (2 power 50)
            EXA = PETA * KILO, // 1000 power 6 (10 power 18)
            EXBI = PEBI * KIBI; // 1024 power 6 (2 power 60)

    private static final DecimalFormat df = new DecimalFormat("#.##");

    public static String binaryBased(long size) {
        if (size < 0) {
            throw new IllegalArgumentException("Argument cannot be negative");
        } else if (size < KIBI) {
            return df.format(size).concat("B");
        } else if (size < MEBI) {
            return df.format(size / KIBI).concat("KiB");
        } else if (size < GIBI) {
            return df.format(size / MEBI).concat("MiB");
        } else if (size < TEBI) {
            return df.format(size / GIBI).concat("GiB");
        } else if (size < PEBI) {
            return df.format(size / TEBI).concat("TiB");
        } else if (size < EXBI) {
            return df.format(size / PEBI).concat("PiB");
        } else {
            return df.format(size / EXBI).concat("EiB");
        }
    }

    public static String decimalBased(long size) {
        if (size < 0) {
            throw new IllegalArgumentException("Argument cannot be negative");
        } else if (size < KILO) {
            return df.format(size).concat("B");
        } else if (size < MEGA) {
            return df.format(size / KILO).concat("KB");
        } else if (size < GIGA) {
            return df.format(size / MEGA).concat("MB");
        } else if (size < TERA) {
            return df.format(size / GIGA).concat("GB");
        } else if (size < PETA) {
            return df.format(size / TERA).concat("TB");
        } else if (size < EXA) {
            return df.format(size / PETA).concat("PB");
        } else {
            return df.format(size / EXA).concat("EB");
        }
    }
}

注意:

  1. 上面的代码冗长而直观。
    • 不会使用循环(仅在编译时不知道需要迭代多少次时才应使用循环)
    • 不会进行不必要的库调用(如StringBuilderMath等)
  2. 上面的代码快速且使用的内存非常少。根据我在个人入门级云服务器上运行的基准测试,它是最快的(虽然性能在这些情况下并不重要,但还是)
  3. 上面的代码是一个好答案的修改版本

0
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);
                }
            }
        }
    }
}

这里有很多冗余。需要一个循环和一个(静态)字符串列表。 - Peter Mortensen

0
使用以下函数获取精确信息。它是通过获取ATM_CashWithdrawl概念的基础生成的。
getFullMemoryUnit(): Total: [123 MB], Max: [1 GB, 773 MB, 512 KB], Free: [120 MB, 409 KB, 304 Bytes]

public static String getFullMemoryUnit(long unit) {
    long BYTE = 1024, KB = BYTE, MB = KB * KB, GB = MB * KB, TB = GB * KB;
    long KILO_BYTE, MEGA_BYTE = 0, GIGA_BYTE = 0, TERA_BYTE = 0;
    unit = Math.abs(unit);
    StringBuffer buffer = new StringBuffer();
    if ( unit / TB > 0 ) {
        TERA_BYTE = (int) (unit / TB);
        buffer.append(TERA_BYTE+" TB");
        unit -= TERA_BYTE * TB;
    }
    if ( unit / GB > 0 ) {
        GIGA_BYTE = (int) (unit / GB);
        if (TERA_BYTE != 0) buffer.append(", ");
        buffer.append(GIGA_BYTE+" GB");
        unit %= GB;
    }
    if ( unit / MB > 0 ) {
        MEGA_BYTE = (int) (unit / MB);
        if (GIGA_BYTE != 0) buffer.append(", ");
        buffer.append(MEGA_BYTE+" MB");
        unit %= MB;
    }
    if ( unit / KB > 0 ) {
        KILO_BYTE = (int) (unit / KB);
        if (MEGA_BYTE != 0) buffer.append(", ");
        buffer.append(KILO_BYTE+" KB");
        unit %= KB;
    }
    if ( unit > 0 ) buffer.append(", "+unit+" Bytes");
    return buffer.toString();
}

我刚刚修改了facebookarchive-StringUtils的代码,以获得以下格式。当您使用apache.hadoop-StringUtils时,您将获得相同的格式。

getMemoryUnit(): Total: [123.0 MB], Max: [1.8 GB], Free: [120.4 MB]

public static String getMemoryUnit(long bytes) {
    DecimalFormat oneDecimal = new DecimalFormat("0.0");
    float BYTE = 1024.0f, KB = BYTE, MB = KB * KB, GB = MB * KB, TB = GB * KB;
    long absNumber = Math.abs(bytes);
    double result = bytes;
    String suffix = " Bytes";
    if (absNumber < MB) {
        result = bytes / KB;
        suffix = " KB";
    } else if (absNumber < GB) {
        result = bytes / MB;
        suffix = " MB";
    } else if (absNumber < TB) {
        result = bytes / GB;
        suffix = " GB";
    }
    return oneDecimal.format(result) + suffix;
}

以上方法的示例用法:

public static void main(String[] args) {
    Runtime runtime = Runtime.getRuntime();
    int availableProcessors = runtime.availableProcessors();

    long heapSize = Runtime.getRuntime().totalMemory();
    long heapMaxSize = Runtime.getRuntime().maxMemory();
    long heapFreeSize = Runtime.getRuntime().freeMemory();

    System.out.format("Total: [%s], Max: [%s], Free: [%s]\n", heapSize, heapMaxSize, heapFreeSize);
    System.out.format("getMemoryUnit(): Total: [%s], Max: [%s], Free: [%s]\n",
            getMemoryUnit(heapSize), getMemoryUnit(heapMaxSize), getMemoryUnit(heapFreeSize));
    System.out.format("getFullMemoryUnit(): Total: [%s], Max: [%s], Free: [%s]\n",
            getFullMemoryUnit(heapSize), getFullMemoryUnit(heapMaxSize), getFullMemoryUnit(heapFreeSize));
}

获取上述格式的字节

Total: [128974848], Max: [1884815360], Free: [126248240]

为了以人类可读的格式显示时间,请使用函数 millisToShortDHMS(long duration)

这里有很多冗余。需要一个循环和一个(静态)字符串列表。 - Peter Mortensen

0

我通常是这样做的:

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);
    }
}

1
通过一些改进,它可以推广到任何度量单位。 - Forcuti Alessandro

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