ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

我有点厌倦了为每个项目编写这种实用方法。 Apache Commons 中是否有为此的静态方法?

如果使用标准化单位,1024 应该变成“1KiB”,1024*1024 应该变成“1MiB”。 en.wikipedia.org/wiki/Binary_prefix
@Pascal Cuoq:感谢您的参考。直到我读到它,我才意识到在欧盟,法律要求我们使用正确的前缀。
@DerMike您提到“直到存在这样的库”。现在这已经成为现实。 :-) stackoverflow.com/questions/3758606/…
@AaronDigulla 你是对的。为什么那个 2 个月大的问题被关闭为重复,而不是这里?
@hc_dev 我想这个 2 个月大的问题已经结束了,因为这个问题的答案要好得多。这些问题都是在 2010 年发布的,另一个问题直到 2013 年才结束。(所以真的应该有一个“合并问题”功能,想想看,把两个问题的答案放在一个地方。)

i
ivan_pozdeev

有趣的事实:这里发布的原始代码段是 Stack Overflow 上有史以来被复制最多的 Java 代码段,但它存在缺陷。它已修复,但它变得混乱。本文全文: 有史以来被复制最多的 Stack Overflow 片段存在缺陷!

来源:Formatting byte size to human readable format | Programming.Guide

国际单位制 (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 Ki = 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)

我更喜欢 1.0 KB。然后很清楚输出需要多少有效数字。 (这似乎也是 Linux 中 du 命令的行为。)
我认为每个人都应该注意,在您的项目中,客户希望看到以 2 为底的值(除以 1024)但具有通用前缀。不是 KiB、MiB、GiB 等。请使用 KB、MB、GB、TB。
@Borys 使用“KB”表示“1024 字节”是错误的。不要那样做。
读者将学习它。他们不熟悉并且可以学习的东西总比出错的好。写入 KB,熟悉它的用户期望 1000,不熟悉的用户期望 1024。
答案完全重写。上面的许多评论已经过时了。
Z
Zarathustra

如果您的项目可以依赖于 org.apache.commons.io,那么 FileUtils.byteCountToDisplaySize(long size) 会起作用。

JavaDoc for this method


我的项目中已经有 commons-io,但由于舍入行为,最终使用了 aioobe 的代码(请参阅 JavaDoc 的链接)
是否有实用程序来执行反向操作。从人类可读的字节数中获取字节数?
不幸的是,此功能不支持区域设置;例如,在法语中,他们总是将字节称为“八位字节”,因此如果您要向法语用户显示一个 100 KB 的文件,正确的标签应该是 100 Ko。
@Tacroy 您可以使用 triava 库中的 UnitFormatter 获取八位字节输出。您可以传递字节、瓦特或八位字节的任何单位。示例,对 github.com/trivago/triava 中的示例稍作修改:UnitFormatter.formatAsUnit(1126, UnitSystem.SI, "o"); // = "1.13 ko" 更多示例见:stackoverflow.com/questions/3758606/…
当 > 1 gb 时,这会四舍五入到最接近的 gb,这意味着您从中获得的精度会有所不同
P
Peter Mortensen

使用 Android 内置类

对于 Android,有一个类,Formatter。只需一行代码,您就完成了。

android.text.format.Formatter.formatShortFileSize(activityContext, bytes);

它类似于 formatFileSize(),但试图生成更短的数字(显示更少的小数)。

android.text.format.Formatter.formatFileSize(activityContext, bytes);

它将内容大小格式化为字节、千字节、兆字节等形式。


绝对应该是ANDROID的最佳答案。不需要额外的库。 +1
我讨厌你必须通过 Context 的事实。
您传入 Context 以便将其转换为用户的当前语言环境。否则它不会是一个非常有用的功能。
在我知道这一点之前,我正在使用公认的答案。需要注意的是,在 Build.VERSION_CODES.N 和更早版本中,使用 1024 的幂,KB = 1024 字节,MB = 1,048,576 字节等。从 O 开始,前缀在 SI 系统中以其标准含义使用,所以 kB = 1000 字节,MB = 1,000,000 字节等。
i
icza

我们可以完全避免使用慢速 Math.pow()Math.log() 方法而不牺牲简单性,因为单位之间的因子(例如,B、KB、MB 等)是 1024,即 2^10。 Long 类有一个方便的 numberOfLeadingZeros() 方法,我们可以使用它来判断大小值属于哪个单位。

关键点:大小单位的距离为 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
<=2                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));
}

P
Peter Mortensen

我最近问了同样的问题:

Format file size as MB, GB, etc.

虽然没有开箱即用的答案,但我可以接受以下解决方案:

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;

public static String convertToStringRepresentation(final long value){
    final long[] dividers = new long[] { T, G, M, K, 1 };
    final String[] units = new String[] { "TB", "GB", "MB", "KB", "B" };
    if(value < 1)
        throw new IllegalArgumentException("Invalid file size: " + value);
    String result = null;
    for(int i = 0; i < dividers.length; i++){
        final long divider = dividers[i];
        if(value >= divider){
            result = format(value, divider, units[i]);
            break;
        }
    }
    return result;
}

private static String format(final long value,
    final long divider,
    final String unit){
    final double result =
        divider > 1 ? (double) value / (double) divider : (double) value;
    return new DecimalFormat("#,##0.#").format(result) + " " + unit;
}

测试代码:

public static void main(final String[] args){
    final long[] l = new long[] { 1l, 4343l, 43434334l, 3563543743l };
    for(final long ll : l){
        System.out.println(convertToStringRepresentation(ll));
    }
}

输出(在我的德语语言环境中):

1 B
4,2 KB
41,4 MB
3,3 GB

我打开了一个 issue requesting this functionality for Google Guava。也许有人会愿意支持它。


为什么 0 是无效的文件大小?
@aioobe 在我的用例中(显示上传文件的大小),但可以说这不是通用的
如果将最后一行更改为返回 NumberFormat.getFormat("#,##0.#").format(result) + " " + unit;它也适用于 GWT!谢谢你,它仍然不在番石榴中。
P
Peter Mortensen
private String bytesIntoHumanReadable(long bytes) {
    long kilobyte = 1024;
    long megabyte = kilobyte * 1024;
    long gigabyte = megabyte * 1024;
    long terabyte = gigabyte * 1024;

    if ((bytes >= 0) && (bytes < kilobyte)) {
        return bytes + " B";

    } else if ((bytes >= kilobyte) && (bytes < megabyte)) {
        return (bytes / kilobyte) + " KB";

    } else if ((bytes >= megabyte) && (bytes < gigabyte)) {
        return (bytes / megabyte) + " MB";

    } else if ((bytes >= gigabyte) && (bytes < terabyte)) {
        return (bytes / gigabyte) + " GB";

    } else if (bytes >= terabyte) {
        return (bytes / terabyte) + " TB";

    } else {
        return bytes + " Bytes";
    }
}

我喜欢这个,因为它易于理解且易于理解。
@Joshua Pinter:是的,但也有很多冗余。它请求一个循环和一个(静态)字符串列表。
你总是可以让事情变得更“高效”,但在某些时候,这可能会以人类读者的清晰度为代价。我认为这是一个很好的权衡。现在,如果您需要支持 2 倍或 3 倍的单位(例如“PB”、“EB”、“ZB”、“YB”),就像其他一些答案一样,那么我认为干燥会很好方法。值得庆幸的是,在我们的应用程序中,我们永远不会超过“GB”,更不用说“TB”了。
C
Community

这是 aioobe's answer 的修改版本。

变化:

语言环境参数,因为某些语言使用 .和其他,作为小数点。

人类可读的代码

private static final String[] SI_UNITS = { "B", "kB", "MB", "GB", "TB", "PB", "EB" };
private static final String[] BINARY_UNITS = { "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB" };

public static String humanReadableByteCount(final long bytes, final boolean useSIUnits, final Locale locale)
{
    final String[] units = useSIUnits ? SI_UNITS : BINARY_UNITS;
    final int base = useSIUnits ? 1000 : 1024;

    // When using the smallest unit no decimal point is needed, because it's the exact number.
    if (bytes < base) {
        return bytes + " " + units[0];
    }

    final int exponent = (int) (Math.log(bytes) / Math.log(base));
    final String unit = units[exponent];
    return String.format(locale, "%.1f %s", bytes / Math.pow(base, exponent), unit);
}

仅针对分隔符传递 Locale 参数的结果有点混杂,但随后不要将单元本地化以考虑也对字节使用不同符号的语言,例如法语。
@Nzall 你的意思是八位字节吗?维基百科说它不再常见。否则,你有参考吗?
作为法国人,我确认“八位字节”仍然很常用;法国人会期待“Ko”、“Mo”、“Go”等。无论如何 i18n 似乎超出了 OP 的范围。如果你真的需要 i18n,你可能不得不使用一些属性文件
L
Lars Bohl

private static final String[] Q = new String[]{"", "K", "M", "G", "T", "P", "E"};

public String getAsString(long bytes)
{
    for (int i = 6; i > 0; i--)
    {
        double step = Math.pow(1024, i);
        if (bytes > step) return String.format("%3.1f %s", bytes / step, Q[i]);
    }
    return Long.toString(bytes);
}

S
Sebastian Hoß

Byte Units 允许您这样做:

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

a
android developer

如果您使用 Android,则只需使用 android.text.format.Formatter.formatFileSize()。优点是它易于使用,并且取决于语言环境以很好地向用户展示它。缺点是它不处理 EB,而且只用于公制单位(每 Kilo 为 1000 字节,无法将其用作 1024 字节)。

或者,这是基于 this popular post 的解决方案:


interface BytesFormatter {
    /**called when the type of the result to format is Long. Example: 123KB
     * @param unitPowerIndex the unit-power we need to format to. Examples: 0 is bytes, 1 is kb, 2 is mb, etc...
     * available units and their order: B,K,M,G,T,P,E
     * @param isMetric true if each kilo==1000, false if kilo==1024
     * */
    fun onFormatLong(valueToFormat: Long, unitPowerIndex: Int, isMetric: Boolean): String

    /**called when the type of the result to format is Double. Example: 1.23KB
     * @param unitPowerIndex the unit-power we need to format to. Examples: 0 is bytes, 1 is kb, 2 is mb, etc...
     * available units and their order: B,K,M,G,T,P,E
     * @param isMetric true if each kilo==1000, false if kilo==1024
     * */
    fun onFormatDouble(valueToFormat: Double, unitPowerIndex: Int, isMetric: Boolean): String
}

/**
 * formats the bytes to a human readable format, by providing the values to format later in the unit that we've found best to fit it
 *
 * @param isMetric true if each kilo==1000, false if kilo==1024
 * */
fun bytesIntoHumanReadable(
    @IntRange(from = 0L) bytesToFormat: Long, bytesFormatter: BytesFormatter,
    isMetric: Boolean = true
): String {
    val units = if (isMetric) 1000L else 1024L
    if (bytesToFormat < units)
        return bytesFormatter.onFormatLong(bytesToFormat, 0, isMetric)
    var bytesLeft = bytesToFormat
    var unitPowerIndex = 0
    while (unitPowerIndex < 6) {
        val newBytesLeft = bytesLeft / units
        if (newBytesLeft < units) {
            val byteLeftAsDouble = bytesLeft.toDouble() / units
            val needToShowAsInteger =
                byteLeftAsDouble == (bytesLeft / units).toDouble()
            ++unitPowerIndex
            if (needToShowAsInteger) {
                bytesLeft = newBytesLeft
                break
            }
            return bytesFormatter.onFormatDouble(byteLeftAsDouble, unitPowerIndex, isMetric)
        }
        bytesLeft = newBytesLeft
        ++unitPowerIndex
    }
    return bytesFormatter.onFormatLong(bytesLeft, unitPowerIndex, isMetric)
}

Sample usage:

// val valueToTest = 2_000L
// val valueToTest = 2_000_000L
// val valueToTest = 2_000_000_000L
// val valueToTest = 9_000_000_000_000_000_000L
// val valueToTest = 9_200_000_000_000_000_000L
val bytesToFormat = Random.nextLong(Long.MAX_VALUE)
val bytesFormatter = object : BytesFormatter {
    val numberFormat = NumberFormat.getNumberInstance(Locale.ROOT).also {
        it.maximumFractionDigits = 2
        it.minimumFractionDigits = 0
    }

    private fun formatByUnit(formattedNumber: String, threePowerIndex: Int, isMetric: Boolean): String {
        val sb = StringBuilder(formattedNumber.length + 4)
        sb.append(formattedNumber)
        val unitsToUse = "B${if (isMetric) "k" else "K"}MGTPE"
        sb.append(unitsToUse[threePowerIndex])
        if (threePowerIndex > 0)
            if (isMetric) sb.append('B') else sb.append("iB")
        return sb.toString()
    }

    override fun onFormatLong(valueToFormat: Long, unitPowerIndex: Int, isMetric: Boolean): String {
        return formatByUnit(String.format("%,d", valueToFormat), unitPowerIndex, isMetric)
    }

    override fun onFormatDouble(valueToFormat: Double, unitPowerIndex: Int, isMetric: Boolean): String {
        //alternative for using numberFormat :
        //val formattedNumber = String.format("%,.2f", valueToFormat).let { initialFormattedString ->
        //    if (initialFormattedString.contains('.'))
        //        return@let initialFormattedString.dropLastWhile { it == '0' }
        //    else return@let initialFormattedString
        //}
        return formatByUnit(numberFormat.format(valueToFormat), unitPowerIndex, isMetric)
    }
}
Log.d("AppLog", "formatting of $bytesToFormat bytes (${String.format("%,d", bytesToFormat)})")
Log.d("AppLog", bytesIntoHumanReadable(bytesToFormat, bytesFormatter))
Log.d("AppLog", "Android:${android.text.format.Formatter.formatFileSize(this, bytesToFormat)}")


您的 for 循环中似乎有一个错误。我认为应该是 unitsCount 而不是 unitsCount-1
@aioobe 但这意味着循环可以在 i==unitsCount 时停止,这意味着 i==6,这意味着“charAt”将失败......
if(result<unit) break; 将在此之前启动。不用担心。 (如果您对其进行测试,您会注意到您可以完全跳过 for 循环条件。)
@aioobe 正确,这是因为我处理“长”变量类型的假设(这是正确的)。此外,它基于这样的假设,即这些单位至少是我所写的。如果你使用更少的单位,它会产生奇怪的结果(更喜欢小于 1 的值,而不是大于 1000 的值)。
@aioobe 正确。我会修好它。顺便说一句,你的算法也可以提供一个奇怪的结果。尝试给它“999999,true”作为参数。它会显示 "1000.0 kB" ,所以它是四舍五入的,但是当人们看到它时,他们会想:为什么它不能显示 1MB ,因为 1000KB=1MB ......你认为应该如何处理?这是因为 String.format,但我不确定应该如何修复它。
a
aminography

• Kotlin 版本通过扩展属性

如果您使用的是 kotlin,则很容易通过这些扩展属性来格式化文件大小。它是无循环的,完全基于纯数学。

HumanizeUtils.kt

import java.io.File
import kotlin.math.log2
import kotlin.math.pow

/**
 * @author aminography
 */

val File.formatSize: String
    get() = length().formatAsFileSize

val Int.formatAsFileSize: String
    get() = toLong().formatAsFileSize

val Long.formatAsFileSize: String
    get() = log2(if (this != 0L) toDouble() else 1.0).toInt().div(10).let {
        val precision = when (it) {
            0 -> 0; 1 -> 1; else -> 2
        }
        val prefix = arrayOf("", "K", "M", "G", "T", "P", "E", "Z", "Y")
        String.format("%.${precision}f ${prefix[it]}B", toDouble() / 2.0.pow(it * 10.0))
    }

用法:

println("0:          " + 0.formatAsFileSize)
println("170:        " + 170.formatAsFileSize)
println("14356:      " + 14356.formatAsFileSize)
println("968542985:  " + 968542985.formatAsFileSize)
println("8729842496: " + 8729842496.formatAsFileSize)

println("file: " + file.formatSize)

结果:

0:          0 B
170:        170 B
14356:      14.0 KB
968542985:  923.67 MB
8729842496: 8.13 GB

file: 6.15 MB

X
XXX
    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 "???";
    }

P
Peter Mortensen

org.springframework.util.unit.DataSize 至少在计算中可以满足这个要求。然后一个简单的装饰器就可以了。


我的要求是打印系统的内存,这对我有帮助,因为我知道它需要始终以 MB 打印。
C
Christian Esken

现在有一个包含单位格式的可用库。我将它添加到 triava 库中,因为唯一的其他现有库似乎是用于 Android 的。

它可以在 3 种不同的系统(SI、IEC、JEDEC)和各种输出选项中以任意精度格式化数字。以下是 triava unit tests 中的一些代码示例:

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

P
Peter Mortensen

创建接口:

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

M
Michel Jung

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

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

P
Peter Mortensen

您可以使用 StringUtilsTraditionalBinarPrefix

public static String humanReadableInt(long number) {
    return TraditionalBinaryPrefix.long2String(number, ””, 1);
}

P
Peter Mortensen

这是一个 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)
}

V
Vishwajit R. Shinde
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;
}

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


为什么不是一个代码块?乍一看,好像缺少“}”。
@PeterMortensen,谢谢,让我知道!是错字,我现在改正了。
i
i30mb1

我使用的方法比接受的答案稍有修改:

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  

T
Tarif Chakder

对于 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 

P
Peter Mortensen

这是上面 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);
    }

从技术上讲,如果我们坚持使用 SI 单位,则此例程适用于任何常规使用的数字。专家还有许多其他很好的答案。假设您正在对网格视图上的数字进行数据绑定,值得从它们中检查性能优化例程。

PS:这是因为这个问题/答案在我做 C# 项目时在谷歌搜索中出现在首位。


P
Peter Mortensen

也许您可以使用此代码(在 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";

这是很多冗余。它请求一个循环和一个(静态)字符串列表。
P
Peter Mortensen

这是转换为 Kotlin 的 the conversion from aioobe

/**
 * 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)
    }
}

S
Samkov Max
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);
                }
            }
        }
    }
}

这是很多冗余。它请求一个循环和一个(静态)字符串列表。
P
Peter Mortensen

使用以下函数获取准确信息。它是根据 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)


这是很多冗余。它请求一个循环和一个(静态)字符串列表。
P
Peter Mortensen

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

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


n
neo7bf

我一般都是这样的,你觉得呢?

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

通过一些改进,它可以推广到任何测量单位。
M
Manu Manjunath

下面是一个快速、简单且易读的代码片段来实现这一点:

/**
 * 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");
        }
    }
}

笔记:

上面的代码冗长而直接。它不使用循环(仅当您不知道在编译期间需要迭代多少次时才应使用循环)它不会进行不必要的库调用(StringBuilder、Math 等) 上面的代码速度很快并且使用的内存非常少.基于在我的个人入门级云机器上运行的基准测试,它是最快的(在这些情况下性能并不重要,但仍然如此)上面的代码是一个很好的答案的修改版本


b
benka
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);

这个答案虽然有效,但它是对@user601806 在此线程中的先前答案的补充:stackoverflow.com/a/4888400/3987745 要使此答案有效,您需要 Apache Commons IO (commons.apache.org/proper/commons-io) 依赖项。