【问题标题】:How can I convert byte size into a human-readable format in Java?如何在 Java 中将字节大小转换为人类可读的格式?
【发布时间】:2011-04-15 02:08:18
【问题描述】:

如何在 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 年才关闭。(所以真的应该有一个“合并问题”功能,想想看,把两个问题的答案放在一个地方。)跨度>

标签: java formatting apache-commons


【解决方案1】:

对于 kotlin 爱好者,请使用此扩展程序

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

现在使用

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

另一种方法

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

现在使用

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

【讨论】:

    【解决方案2】:

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

    import java.util.Locale;
    
    public final class Bytes {
    
      private Bytes() {
      }
    
      public static String format(long value, Locale locale) {
        if (value < 1024) {
          return value + " B";
        }
        int z = (63 - Long.numberOfLeadingZeros(value)) / 10;
        return String.format(locale, "%.1f %siB", (double) value / (1L << (z * 10)), " KMGTPE".charAt(z));
      }
    }
    

    测试:

    Locale locale = Locale.getDefault()
    System.out.println(Bytes.format(1L, locale))
    System.out.println(Bytes.format(2L * 1024, locale))
    System.out.println(Bytes.format(3L * 1024 * 1024, locale))
    System.out.println(Bytes.format(4L * 1024 * 1024 * 1024, locale))
    System.out.println(Bytes.format(5L * 1024 * 1024 * 1024 * 1024, locale))
    System.out.println(Bytes.format(6L * 1024 * 1024 * 1024 * 1024 * 1024, locale))
    System.out.println(Bytes.format(Long.MAX_VALUE, locale))
    

    输出:

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

    【讨论】:

      【解决方案3】:

      下面是一个快速、简单、易读的代码sn-p来实现:

      /**
       * Converts byte size to human readable strings (also declares useful constants)
       *
       * @see <a href="https://en.wikipedia.org/wiki/File_size">File size</a>
       */
      @SuppressWarnings("SpellCheckingInspection")
      public class HumanReadableSize {
          public static final double
                  KILO = 1000L, // 1000 power 1 (10 power 3)
                  KIBI = 1024L, // 1024 power 1 (2 power 10)
                  MEGA = KILO * KILO, // 1000 power 2 (10 power 6)
                  MEBI = KIBI * KIBI, // 1024 power 2 (2 power 20)
                  GIGA = MEGA * KILO, // 1000 power 3 (10 power 9)
                  GIBI = MEBI * KIBI, // 1024 power 3 (2 power 30)
                  TERA = GIGA * KILO, // 1000 power 4 (10 power 12)
                  TEBI = GIBI * KIBI, // 1024 power 4 (2 power 40)
                  PETA = TERA * KILO, // 1000 power 5 (10 power 15)
                  PEBI = TEBI * KIBI, // 1024 power 5 (2 power 50)
                  EXA = PETA * KILO, // 1000 power 6 (10 power 18)
                  EXBI = PEBI * KIBI; // 1024 power 6 (2 power 60)
      
          private static final DecimalFormat df = new DecimalFormat("#.##");
      
          public static String binaryBased(long size) {
              if (size < 0) {
                  throw new IllegalArgumentException("Argument cannot be negative");
              } else if (size < KIBI) {
                  return df.format(size).concat("B");
              } else if (size < MEBI) {
                  return df.format(size / KIBI).concat("KiB");
              } else if (size < GIBI) {
                  return df.format(size / MEBI).concat("MiB");
              } else if (size < TEBI) {
                  return df.format(size / GIBI).concat("GiB");
              } else if (size < PEBI) {
                  return df.format(size / TEBI).concat("TiB");
              } else if (size < EXBI) {
                  return df.format(size / PEBI).concat("PiB");
              } else {
                  return df.format(size / EXBI).concat("EiB");
              }
          }
      
          public static String decimalBased(long size) {
              if (size < 0) {
                  throw new IllegalArgumentException("Argument cannot be negative");
              } else if (size < KILO) {
                  return df.format(size).concat("B");
              } else if (size < MEGA) {
                  return df.format(size / KILO).concat("KB");
              } else if (size < GIGA) {
                  return df.format(size / MEGA).concat("MB");
              } else if (size < TERA) {
                  return df.format(size / GIGA).concat("GB");
              } else if (size < PETA) {
                  return df.format(size / TERA).concat("TB");
              } else if (size < EXA) {
                  return df.format(size / PETA).concat("PB");
              } else {
                  return df.format(size / EXA).concat("EB");
              }
          }
      }
      

      注意:

      1. 以上代码冗长而直接。
        • 它确实使用循环(只有在编译期间不知道需要迭代多少次时才应使用循环)
        • 确实不会进行不必要的库调用(StringBuilderMath 等)
      2. 上面的代码速度很快并且使用的内存非常少。根据在我的个人入门级云机器上运行的基准,它是最快的(在这些情况下性能并不重要,但仍然如此)
      3. 以上代码是其中一个好答案的修改版本

      【讨论】:

        【解决方案4】:

        有趣的事实: 此处发布的原始 sn-p 是 Stack Overflow 上有史以来复制最多的 Java sn-p,但它存在缺陷。它已修复,但变得一团糟。

        本文全文:The most copied Stack Overflow snippet of all time is flawed!

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

        二进制 (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。
        • 完全重写了答案。上述许多 cmets 已过时。
        【解决方案5】:

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

        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  
        

        【讨论】:

          【解决方案6】:

          Kotlin Version 通过Extension Property

          如果您使用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
          

          【讨论】:

            【解决方案7】:

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

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

            【讨论】:

            • 经过一些改进,它可以推广到任何测量单位。
            【解决方案8】:
            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,感谢您让我知道这件事!这是错字,我现在已经更正了。
            【解决方案9】:

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

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

            【讨论】:

              【解决方案10】:

              我们可以完全避免使用缓慢的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));
              }
              

              【讨论】:

                【解决方案11】:

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

                【讨论】:

                • 我的要求是打印系统的内存,这对我有帮助,因为我知道它需要始终以 MB 为单位打印。
                【解决方案12】:

                这里是the conversion from aioobe 转换为 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)
                    }
                }
                

                【讨论】:

                  【解决方案13】:

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

                  【讨论】:

                  • 那是很多冗余。它请求一个循环和一个(静态)字符串列表。
                  【解决方案14】:

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

                  【讨论】:

                    【解决方案15】:

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

                    【讨论】:

                    • 那是很多冗余。它请求一个循环和一个(静态)字符串列表。
                    【解决方案16】:

                    你可以使用StringUtilsTraditionalBinarPrefix

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

                    【讨论】:

                      【解决方案17】:

                      创建接口:

                      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
                      

                      【讨论】:

                        【解决方案18】:

                        这是上面 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 单位,则此例程适用于任何常规使用的数字。专家还有许多其他很好的答案。假设您正在对 gridviews 上的数字进行数据绑定,值得从它们中检查性能优化例程。

                        PS:发布此问题是因为在我做 C# 项目时,这个问题/答案在 Google 搜索中排名靠前。

                        【讨论】:

                          【解决方案19】:
                          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”了。
                          【解决方案20】:

                          使用 Android 内置类

                          对于 Android,有一个类,Formatter。只需一行代码即可完成。

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

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

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

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

                          【讨论】:

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

                          如果你使用Android,你可以简单地使用android.text.format.Formatter.formatFileSize()

                          或者,这里有一个基于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;
                            }
                          

                          或者在 Kotlin 中:

                          /**
                           * formats the bytes to a human readable format
                           *
                           * @param si true if each kilo==1000, false if kilo==1024
                           */
                          @SuppressLint("DefaultLocale")
                          fun humanReadableByteCount(bytes: Long, si: Boolean): String? {
                              val unit = if (si) 1000.0 else 1024.0
                              if (bytes < unit)
                                  return "$bytes B"
                              var result = bytes.toDouble()
                              val unitsToUse = (if (si) "k" else "K") + "MGTPE"
                              var i = 0
                              val unitsCount = unitsToUse.length
                              while (true) {
                                  result /= unit
                                  if (result < unit || i == unitsCount - 1)
                                      break
                                  ++i
                              }
                              return with(StringBuilder(9)) {
                                  append(String.format("%.1f ", result))
                                  append(unitsToUse[i])
                                  if (si) append('B') else append("iB")
                              }.toString()
                          }
                          

                          【讨论】:

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

                          我最近问了同样的问题:

                          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!谢谢你,它仍然不在 Guava 中。
                          【解决方案23】:
                          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);
                                          }
                                      }
                                  }
                              }
                          }
                          

                          【讨论】:

                          • 那是很多冗余。它请求一个循环和一个(静态)字符串列表。
                          【解决方案24】:

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

                          【讨论】:

                            【解决方案25】:

                            现在有一个可用的库包含单位格式。我将它添加到 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
                            

                            【讨论】:

                              【解决方案26】:

                              如果您的项目可以依赖org.apache.commons.ioFileUtils.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,这意味着您从中得到的精度会有所不同
                              【解决方案27】:

                              这是aioobe's answer的修改版。

                              变化:

                              • Locale 参数,因为有些语言使用. 而其他语言使用, 作为小数点。
                              • 人类可读的代码

                              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,你可能不得不使用一些属性文件
                              【解决方案28】:
                                  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 "???";
                                  }
                              

                              【讨论】:

                                【解决方案29】:
                                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);
                                

                                【讨论】:

                                【解决方案30】:
                                
                                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);
                                }
                                

                                【讨论】:

                                  猜你喜欢
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 2014-12-24
                                  • 2012-05-12
                                  相关资源
                                  最近更新 更多