How to convert byte size into human readable forma

2019-01-01 05:46发布

问题:

How to convert byte size into 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. Are there any static methods in Apache Commons for this?

回答1:

Here is my go at it (no loops and handles both SI units and binary units):

public static String humanReadableByteCount(long bytes, boolean si) {
    int unit = si ? 1000 : 1024;
    if (bytes < unit) return bytes + \" B\";
    int exp = (int) (Math.log(bytes) / Math.log(unit));
    String pre = (si ? \"kMGTPE\" : \"KMGTPE\").charAt(exp-1) + (si ? \"\" : \"i\");
    return String.format(\"%.1f %sB\", bytes / Math.pow(unit, exp), pre);
}

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)

Related article: Java: Formatting byte size to human readable format



回答2:

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

JavaDoc for this method



回答3:

Use Android builtin Class

For Android there is a class Formatter. Just one like 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);

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



回答4:

We can completely avoid using the slow Math.pow() and Math.log() methods without sacrificing simplicity since the factor between the units (e.g. 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 1 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));
}


回答5:

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

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



回答6:

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


回答7:

If you use Android, you can simply use Formatter.formatFileSize() .

Alternativey, here\'s a solution based on this popular post :

  /**
   * formats the bytes to a human readable format
   *
   * @param si true if each kilo==1000, false if kilo==1024
   */
  @SuppressLint(\"DefaultLocale\")
  public static String humanReadableByteCount(final long bytes,final boolean si)
    {
    final int unit=si ? 1000 : 1024;
    if(bytes<unit)
      return bytes+\" B\";
    double result=bytes;
    final String unitsToUse=(si ? \"k\" : \"K\")+\"MGTPE\";
    int i=0;
    final int unitsCount=unitsToUse.length();
    while(true)
      {
      result/=unit;
      if(result<unit)
        break;
      // check if we can go further:
      if(i==unitsCount-1)
        break;
      ++i;
      }
    final StringBuilder sb=new StringBuilder(9);
    sb.append(String.format(\"%.1f \",result));
    sb.append(unitsToUse.charAt(i));
    if(si)
      sb.append(\'B\');
    else sb.append(\'i\').append(\'B\');
    final String resultStr=sb.toString();
    return resultStr;
    }


回答8:


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


回答9:

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


回答10:

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


回答11:

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


回答12:

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


回答13:

I know it\'s too late to update this post! but I had some fun with this:

Create an interface:

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

Create 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


回答14:

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

Just add more file units (if any missing), and you will see unit size upto that unit (if your file has that much length) System.out.println(\"File size in proper format: \" + bytes + \" \" + fileSizeUnits[index]); sizeToReturn = String.valueOf(bytes) + \" \" + fileSizeUnits[index]; return sizeToReturn; }



回答15:

Here\'s the C# .net equivalent for Java correct consensus answer above. (there\'s another below which have shorter codes)

    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, its worth to check out performance optimized routines from them.

PS: Posted because this question/answer came up on top on google search while I am doing C# project.



回答16:

In the off-chance it saves someone a bit of time, or maybe just for a bit of fun, 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)
}


回答17:

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


回答18:

Have you tried 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.



回答19:

You can use StringUtils’s TraditionalBinarPrefix:

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


回答20:

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\";