ChatGPT解决这个技术问题 Extra ChatGPT

How can I convert byte size into a human-readable format in Java?

How can I convert byte size into a human-readable format in Java?

Like 1024 should become "1 Kb" and 1024*1024 should become "1 Mb".

I am kind of sick of writing this utility method for each project. Is there a static method in Apache Commons for this?

If you use the standardized units, 1024 should become "1KiB" and 1024*1024 should become "1MiB". en.wikipedia.org/wiki/Binary_prefix
@Pascal Cuoq: Thanks for the reference. I didn't realise until I read it that here in the EU we are required to use the correct prefixes by law.
@DerMike You mentioned that "Until such a library exists". This has now become true. :-) stackoverflow.com/questions/3758606/…
@AaronDigulla You are right. Why was that 2 months older question closed as duplicate, and not this here?
@hc_dev I imagine the 2-months-older question was closed because this one had far better answers. These questions were both posted in 2010, the other one wasn't closed until 2013. (SO should really have a "merge questions" feature, come to think of it, to pull the answers from both together into one place.)

i
ivan_pozdeev

Fun fact: The original snippet posted here was the most copied Java snippet of all time on Stack Overflow, and it was flawed. It was fixed, but it got messy. Full story in this article: The most copied Stack Overflow snippet of all time is flawed!

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

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

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

Example output:

                              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)

I prefer 1.0 KB. Then it's clear how many significant figures the output entails. (This also seems to be the behavior of for instance the du command in Linux.)
I think every one should note that in your project customer want see values in base 2 (devided by 1024) but with common prefix. Not KiB, MiB, GiB etc. Use KB, MB, GB, TB for it.
@Borys Using "KB" to mean "1024 bytes" is wrong. Don't do that.
Readers will learn it. Better something they are unfamiliar with and can learn it than having something wrong. Writing KB a user who is familiar with it will expect 1000 and a user who is unfamiliar will expect 1024.
Answer rewritten entirely. Many of the above comments are obsolete.
Z
Zarathustra

FileUtils.byteCountToDisplaySize(long size) would work if your project can depend on org.apache.commons.io.

JavaDoc for this method


I already have commons-io on my project, but ended up using aioobe's code, because of the rounding behavior (see the link for JavaDoc)
is there a utility to do the reverse operation. Getting byte count from human readable byte count?
Unfortunately this function is not locale-aware; in French, for example, they always call bytes "octets" so if you're going to display a 100 KB file to a French user the correct label would be 100 Ko.
@Tacroy You can get octets output with the UnitFormatter in the triava library. You can pass any unit for bytes, Watts or octets. Example, slightly modified from the examples in github.com/trivago/triava: UnitFormatter.formatAsUnit(1126, UnitSystem.SI, "o"); // = "1.13 ko" More examples in: stackoverflow.com/questions/3758606/…
this rounds to the nearest gb when > 1 gb, which means that the precision you get out of it varies
P
Peter Mortensen

Use an Android built-in class

For Android, there is a class, Formatter. Just one line of code and you are done.

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

It is like formatFileSize(), but trying to generate shorter numbers (showing fewer decimals).

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

It formats a content size to be in the form of bytes, kilobytes, megabytes, etc.


should be the best answer for ANDROID definitively. No extra libraries needed. +1
I hate the fact you have to pass in Context.
You pass in Context so it's translated to the user's current Locale. Otherwise it wouldn't be a very useful function.
I was using the accepted answer before I know this. Just to be noted, In Build.VERSION_CODES.N and earlier, powers of 1024 are used instead, with KB = 1024 bytes, MB = 1,048,576 bytes, etc. As of O, the prefixes are used in their standard meanings in the SI system, so kB = 1000 bytes, MB = 1,000,000 bytes, etc.
i
icza

We can completely avoid using the slow Math.pow() and Math.log() methods without sacrificing simplicity since the factor between the units (for example, B, KB, MB, etc.) is 1024 which is 2^10. The Long class has a handy numberOfLeadingZeros() method which we can use to tell which unit the size value falls in.

Key point: Size units have a distance of 10 bits (1024 = 2^10) meaning the position of the highest one bit–or in other words the number of leading zeros–differ by 10 (Bytes = KB*1024, KB = MB*1024, etc.).

Correlation between number of leading zeros and size unit:

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

The final code:

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

I asked the same question recently:

Format file size as MB, GB, etc.

While there is no out-of-the-box answer, I can live with the solution:

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

Test code:

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

Output (on my German locale):

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

I have opened an issue requesting this functionality for Google Guava. Perhaps someone would care to support it.


Why is 0 an invalid file-size?
@aioobe it was in my use case (displaying the size of an uploaded file), but arguably that's not universal
If you change the last line to return NumberFormat.getFormat("#,##0.#").format(result) + " " + unit; it works in GWT too! Thanks for this, it's still not in Guava.
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";
    }
}

I like this because it's easy to follow and easy to understand.
@Joshua Pinter: Yes, but there is also a lot of redundancy. It begs for a loop and a (static) list of strings.
You can always make things more "efficient" but at some point that can come at a cost of clarity to the human reader. I think this is a great trade off. Now, if you needed to support 2x or 3x the units (e.g. "PB", "EB", "ZB", "YB"), like some of the other answers, do, then I think DRYing things up would be a good approach. Thankfully, in our application we'll never go past "GB", let alone "TB".
C
Community

This is a modified version of aioobe's answer.

Changes:

Locale parameter, because some languages use . and others , as decimal point.

human-readable code

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

It is a bit mixed results to pass along a Locale parameter just for the separator symbols, but then don't also localize the unit to account for languages that also use a different symbol for Bytes, like the French.
@Nzall Do you mean the octet? Wikipedia is stating it's no longer common. Else, do you have a reference?
as a French I confirm "octet" is still very used; french people will expect "Ko", "Mo", "Go", etc. Anyway i18n seems out of scope for OP. If you really need i18n you would probably have to use some properties files
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 allows you to do it like this:

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

I have written another library called storage-units that allows you to do it like this:

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

In case you want to force a certain unit, do this:

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

If you use Android, you can simply use android.text.format.Formatter.formatFileSize(). The advantage is that it's easy to use it, and it depends on the locale to show it nicely for the user. The disadvantage is that it doesn't handle EB, and that it's only used for the metric units (each Kilo is 1000 bytes, without the ability to use it as 1024 bytes).

Alternatively, here's a solution based on 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)}")


You seem to have an off-by-one error in your for-loop. I think it should be unitsCount and not unitsCount-1.
@aioobe but this means the loop can stop when i==unitsCount, which means i==6, which means "charAt" will fail...
if(result<unit) break; will kick in before that. No worries. (If you test it, you'll notice that you can skip the for-loop condition entirely.)
@aioobe Correct, that's because of the assumption (which is correct) that I handle "long" variable type. Also, it's based on the assumption that the units will be at least what I've written. If you use less units, it will produce weird results (will prefer less-than-1 values, rather than larger-than-1000 values).
@aioobe Correct. I will fix it. BTW, your algorithm can also provide a weird result. try giving it "999999,true" as the arguments. it will show "1000.0 kB" , so it's rounded, but when people see it, they can wonder: why can't it show 1MB , as 1000KB=1MB ... How do you think this should be handled? It's because of the String.format, but I'm not sure how it should be fixed.
a
aminography

• Kotlin Version via Extension Property

If you are using kotlin, it's pretty easy to format file size by these extension properties. It is loop-free and completely based on pure math.

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

Usage:

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

println("file: " + file.formatSize)

Result:

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 could suit this requirement at least for the calculation. Then a simple decorator will do.


My requirement was to print the memory of the system and this helped me as I know it needs to be printed in MB always.
C
Christian Esken

There is now one library available that contains unit formatting. I added it to the triava library, as the only other existing library seems to be one for Android.

It can format numbers with arbitrary precision, in 3 different systems (SI, IEC, JEDEC) and various output options. Here are some code examples from the triava unit tests:

UnitFormatter.formatAsUnit(1126, UnitSystem.SI, "B");
// = "1.13kB"
UnitFormatter.formatAsUnit(2094, UnitSystem.IEC, "B");
// = "2.04KiB"

Printing exact kilo, mega values (here with W = Watt):

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

You can pass a DecimalFormat to customize the output:

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

For arbitrary operations on kilo or mega values, you can split them into components:

UnitComponent uc = new  UnitComponent(123_345_567_789L, UnitSystem.SI);
int kilos = uc.kilo(); // 567
int gigas = uc.giga(); // 123

P
Peter Mortensen

Create an interface:

public interface IUnits {
    public String format(long size, String pattern);
    public long getUnitSize();
}

Create the StorageUnits class:

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.#");
    }
}

Call it:

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

Output:

21.4kB
2GB

M
Michel Jung

Yet another concise solution without loop but with locale-sensitive formatting and correct binary prefixes:

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

Test:

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

Output:

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

P
Peter Mortensen

You can use StringUtils’s TraditionalBinarPrefix:

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

P
Peter Mortensen

Here's a Go version. For simplicity, I've only included the binary output case.

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

Just add more file units (if any missing), and you will see unit size up to that unit (if your file has that much length):


Why not one code block? At first glance, it appears as if there is a missing "}.
@PeterMortensen, Thanks, for letting me know about it! It was typo, I have corrected it now.
i
i30mb1

I'm using slighly modified method than accepted answer :

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

Because i want to see another output :

                              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

For kotlin lover use this extension

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]
}

Now use

val size : Long = 90836457
val readbleString = size.readableFormat()

Another approach

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]
    }

Now use

val size : Long = 90836457
val readbleString = size.formatSize 

P
Peter Mortensen

Here's the C# .NET equivalent for Java correct consensus answer above (there's another below which has shorter code):

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

Technically speaking, if we stick to SI units, this routine works for any regular use of numbers. There are many other good answers from experts. Suppose you are doing databinding of numbers on gridviews, it's worth to check out performance optimized routines from them.

PS: This was posted because this question/answer came up on top on a Google search while I was doing a C# project.


P
Peter Mortensen

Maybe you can use this code (in 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";

That is a lot of redundancy. It begs for a loop and a (static) list of strings.
P
Peter Mortensen

Here is the conversion from aioobe converted to 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)
    }
}

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

That is a lot of redundancy. It begs for a loop and a (static) list of strings.
P
Peter Mortensen

Use the following function to get exact information. It is generated by taking the base of the ATM_CashWithdrawl concept.

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

I have just modified the code of facebookarchive-StringUtils to get the below format. The same format you will get when you use 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;
}

Example usage of the above methods:

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

Bytes to get the above format

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

In order to display time in a human-readable format, use the function millisToShortDHMS(long duration).


That is a lot of redundancy. It begs for a loop and a (static) list of strings.
P
Peter Mortensen

Try JSR 363. Its unit extension modules like Unicode CLDR (in GitHub: uom-systems) do all that for you.

You can use MetricPrefix included in every implementation or BinaryPrefix (comparable to some of the examples above) and if you e.g. live and work in India or a nearby country, IndianPrefix (also in the common module of uom-systems) allows you to use and format "Crore Bytes" or "Lakh Bytes", too.


n
neo7bf

I usually do it in this way, what do you think?

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

With some improvement it could be generalized to any unit of measurement.
M
Manu Manjunath

Below is a fast, simple and readable code snippet to achieve this:

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

Note:

Above code is verbose and straightforward. It does not use loops (loops should be used only when you do not know how many times you need to iterate during compilation time) It does not make unnecessary library calls (StringBuilder, Math etc.) Above code is fast and uses very less memory. Based on benchmarks run on my personal entry-level cloud machine, it's the fastest (not that performance matters in these cases, but still) Above code is a modified version of one of the good answers


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

This answer, while it works, is a complement to a previous answer in this thread by @user601806: stackoverflow.com/a/4888400/3987745 For this answer to work, you need Apache Commons IO (commons.apache.org/proper/commons-io) dependency.