【问题标题】:Format bytes to kilobytes, megabytes, gigabytes将字节格式化为千字节、兆字节、千兆字节
【发布时间】:2025-04-30 12:55:02
【问题描述】:

场景:各种文件的大小以字节形式存储在数据库中。将此大小信息格式化为千字节、兆字节和千兆字节的最佳方法是什么?例如,我有一个 Ubuntu 显示为“5.2 MB(5445632 字节)”的 MP3。我如何在网页上将其显示为“5.2 MB”,并且将小于 1 MB 的文件显示为 KB,将 1 GB 及以上的文件显示为 GB?

【问题讨论】:

  • 我相信你应该创建一个函数来做这个。只需将数字除以 1024 并查看结果。如果超过 1024 则再除。

标签: php


【解决方案1】:

这是一个使用log10的选项:

<?php

function format_number(float $d): string {
   $e = (int)(log10($d) / 3);
   return sprintf('%.3f', $d / 1e3 ** $e) . ['', ' k', ' M', ' G'][$e];
}

$s = format_number(9012345678);
var_dump($s == '9.012 G');

https://php.net/function.log10

【讨论】:

  • 这个答案缺少教育解释。
  • 您希望浮点值作为传入字节数吗?
【解决方案2】:

灵活的解决方案:

function size($size, array $options=null) {

    $o = [
        'binary' => false,
        'decimalPlaces' => 2,
        'decimalSeparator' => '.',
        'thausandsSeparator' => '',
        'maxThreshold' => false, // or thresholds key
        'suffix' => [
            'thresholds' => ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'],
            'decimal' => ' {threshold}B',
            'binary' => ' {threshold}iB',
            'bytes' => ' B'
        ]
    ];

    if ($options !== null)
        $o = array_replace_recursive($o, $options);

    $base = $o['binary'] ? 1024 : 1000;
    $exp = $size ? floor(log($size) / log($base)) : 0;

    if (($o['maxThreshold'] !== false) &&
        ($o['maxThreshold'] < $exp)
    )
        $exp = $o['maxThreshold'];

    return !$exp
        ? (round($size) . $o['suffix']['bytes'])
        : (
            number_format(
                $size / pow($base, $exp),
                $o['decimalPlaces'],
                $o['decimalSeparator'],
                $o['thausandsSeparator']
            ) .
            str_replace(
                '{threshold}',
                $o['suffix']['thresholds'][$exp],
                $o['suffix'][$o['binary'] ? 'binary' : 'decimal']
            )
        );
}

var_dump(size(disk_free_space('/')));
// string(8) "14.63 GB"
var_dump(size(disk_free_space('/'), ['binary' => true]));
// string(9) "13.63 GiB"
var_dump(size(disk_free_space('/'), ['maxThreshold' => 2]));
// string(11) "14631.90 MB"
var_dump(size(disk_free_space('/'), ['binary' => true, 'maxThreshold' => 2]));
// string(12) "13954.07 MiB"

【讨论】:

    【解决方案3】:

    我将所有输入转换为字节,然后转换为所需的任何输出。另外,我使用了一个辅助函数来获取基数 1000 或 1024,但让它灵活地决定在流行类型上使用 1024(没有“i”,比如 MB 而不是 MiB)。

        public function converte_binario($size=0,$format_in='B',$format_out='MB',$force_in_1024=false,$force_out_1024=false,$precisao=5,$return_format=true,$decimal=',',$centena=''){
        $out = false;
    
        if( (is_numeric($size)) && ($size>0)){
            $in_data = $this->converte_binario_aux($format_in,$force_in_1024);
            $out_data = $this->converte_binario_aux($format_out,$force_out_1024);
    
            // se formato de entrada e saída foram encontrados
            if( ((isset($in_data['sucesso'])) && ($in_data['sucesso']==true)) && ((isset($out_data['sucesso'])) && ($out_data['sucesso']==true))){
                // converte formato de entrada para bytes.
                $size_bytes_in = $size * (pow($in_data['base'], $in_data['pot']));
                $size_byte_out = (pow($out_data['base'], $out_data['pot']));
                // transforma bytes na unidade de destino
                $out = number_format($size_bytes_in / $size_byte_out,$precisao,$decimal,$centena);
                if($return_format){
                    $out .= $format_out;
                }
            }
        }
        return $out;
    }
    
    public function converte_binario_aux($format=false,$force_1024=false){
        $out = [];
        $out['sucesso'] = false;
        $out['base'] = 0;
        $out['pot'] = 0;
        if((is_string($format) && (strlen($format)>0))){
            $format = trim(strtolower($format));
            $units_1000 = ['b','kb' ,'mb' ,'gb' ,'tb' ,'pb' ,'eb' ,'zb' ,'yb' ];
            $units_1024 = ['b','kib','mib','gib','tib','pib','eib','zib','yib'];
            $pot = array_search($format,$units_1000);
            if( (is_numeric($pot)) && ($pot>=0)){
                $out['pot'] = $pot;
                $out['base'] = 1000;
                $out['sucesso'] = true;
            }
            else{
                $pot = array_search($format,$units_1024);
                if( (is_numeric($pot)) && ($pot>=0)){
                    $out['pot'] = $pot;
                    $out['base'] = 1024;
                    $out['sucesso'] = true;
                }
            }
            if($force_1024){
                $out['base'] = 1024;
            }
        }
        return $out;
    }
    

    【讨论】:

      【解决方案4】:

      虽然有点陈旧,但这个库提供了一个测试和强大的转换 API:

      https://github.com/gabrielelana/byte-units

      安装后:

      \ByteUnits\Binary::bytes(1024)->format();
      
      // Output: "1.00KiB"
      

      并在另一个方向转换:

      \ByteUnits\Binary::parse('1KiB')->numberOfBytes();
      
      // Output: "1024"
      

      除了基本的转换之外,它还提供了加法、减法、比较等方法。

      我与这个图书馆没有任何关系。

      【讨论】:

        【解决方案5】:

        基于Leo's answer,添加

        • 支持负数
        • 支持 0

        如果您希望最大单位为 Mega,请更改为 $units = explode(' ', ' K M');


        function formatUnit($value, $precision = 2) {
            $units = explode(' ', ' K M G T P E Z Y');
        
            if ($value < 0) {
                return '-' . formatUnit(abs($value));
            }
        
            if ($value < 1) {
                return $value . $units[0];
            }
        
            $power = min(
                floor(log($value, 1024)),
                count($units) - 1
            );
        
            return round($value / pow(1024, $power), $precision) . $units[$power];
        }
        

        【讨论】:

          【解决方案6】:
          function convertToReadableSize($size)
          {
            $base = log($size) / log(1024);
            $suffix = array("B", "KB", "MB", "GB", "TB");
            $f_base = floor($base);
            return round(pow(1024, $base - floor($base)), 1) . $suffix[$f_base];
          }
          

          只需调用函数

          echo convertToReadableSize(1024); // Outputs '1KB'
          echo convertToReadableSize(1024 * 1024); // Outputs '1MB'
          

          【讨论】:

          • 这个答案缺少教育解释。
          • 相当不错,但缺少对 $size=0 的一些处理
          【解决方案7】:

          我开发了自己的函数,可以将人类可读的内存大小转换为不同的大小。

          function convertMemorySize($strval, string $to_unit = 'b')
          {
              $strval    = strtolower(str_replace(' ', '', $strval));
              $val       = floatval($strval);
              $to_unit   = strtolower(trim($to_unit))[0];
              $from_unit = str_replace($val, '', $strval);
              $from_unit = empty($from_unit) ? 'b' : trim($from_unit)[0];
              $units     = 'kmgtph';  // (k)ilobyte, (m)egabyte, (g)igabyte and so on...
          
          
              // Convert to bytes
              if ($from_unit !== 'b')
                  $val *= 1024 ** (strpos($units, $from_unit) + 1);
          
          
              // Convert to unit
              if ($to_unit !== 'b')
                  $val /= 1024 ** (strpos($units, $to_unit) + 1);
          
          
              return $val;
          }
          
          
          convertMemorySize('1024Kb', 'Mb');  // 1
          convertMemorySize('1024', 'k')      // 1
          convertMemorySize('5.2Mb', 'b')     // 5452595.2
          convertMemorySize('10 kilobytes', 'bytes') // 10240
          convertMemorySize(2048, 'k')        // By default convert from bytes, result is 2
          

          此函数接受任何内存大小的缩写,例如“Megabyte, MB, Mb, mb, m, kilobyte, K, KB, b, Terabyte, T....”,因此不会出现拼写错误。

          【讨论】:

            【解决方案8】:

            获取人类文件大小的极其简单的函数。

            原文来源:http://php.net/manual/de/function.filesize.php#106569

            复制/粘贴代码:

            <?php
            function human_filesize($bytes, $decimals = 2) {
              $sz = 'BKMGTP';
              $factor = floor((strlen($bytes) - 1) / 3);
              return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$sz[$factor];
            }
            ?>
            

            【讨论】:

              【解决方案9】:

              我想我会添加我正在使用的两个提交者代码的网格(使用此线程中的 John Himmelman 的代码,并使用 Eugene Kuzmenko 的代码)。

              function swissConverter($value, $format = true, $precision = 2) {
                  //Below converts value into bytes depending on input (specify mb, for 
                  //example)
                  $bytes = preg_replace_callback('/^\s*(\d+)\s*(?:([kmgt]?)b?)?\s*$/i', 
                  function ($m) {
                      switch (strtolower($m[2])) {
                        case 't': $m[1] *= 1024;
                        case 'g': $m[1] *= 1024;
                        case 'm': $m[1] *= 1024;
                        case 'k': $m[1] *= 1024;
                      }
                      return $m[1];
                      }, $value);
                  if(is_numeric($bytes)) {
                      if($format === true) {
                          //Below converts bytes into proper formatting (human readable 
                          //basically)
                          $base = log($bytes, 1024);
                          $suffixes = array('', 'KB', 'MB', 'GB', 'TB');   
              
                          return round(pow(1024, $base - floor($base)), $precision) .' '. 
                                   $suffixes[floor($base)];
                      } else {
                          return $bytes;
                      }
                  } else {
                      return NULL; //Change to prefered response
                  }
              }
              

              这使用 Eugene 的代码将 $value 格式化为字节(我将数据保存在 MB 中,因此它将我的数据:10485760 MB 转换为 10995116277760) - 然后它使用 John 的代码将其转换为正确的显示值(10995116277760 转换为 10 TB)。

              我发现这真的很有帮助 - 非常感谢两位提交者!

              【讨论】:

                【解决方案10】:

                简单的功能

                function formatBytes($size, $precision = 0){
                    $unit = ['Byte','KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'];
                
                    for($i = 0; $size >= 1024 && $i < count($unit)-1; $i++){
                        $size /= 1024;
                    }
                
                    return round($size, $precision).' '.$unit[$i];
                }
                
                echo formatBytes('1876144', 2);
                //returns 1.79 MiB
                

                【讨论】:

                • 这个答案缺少教育解释。
                【解决方案11】:

                我不知道你为什么要把它弄得和其他人一样复杂。

                以下代码比其他使用 log 函数的解决方案更容易理解,并且速度快约 25%(称为函数 20 Mio.times with different parameters)

                function formatBytes($bytes, $precision = 2) {
                    $units = ['Byte', 'Kilobyte', 'Megabyte', 'Gigabyte', 'Terabyte'];
                    $i = 0;
                
                    while($bytes > 1024) {
                        $bytes /= 1024;
                        $i++;
                    }
                    return round($bytes, $precision) . ' ' . $units[$i];
                }
                

                【讨论】:

                  【解决方案12】:

                  另一个精简的实现,可以转换为基数 1024(二进制)或基数 1000(十进制),并且也可以处理非常大的数字,因此使用 bc 库:

                  function renderSize($byte,$precision=2,$mibi=true)
                  {
                      $base = (string)($mibi?1024:1000);
                      $labels = array('K','M','G','T','P','E','Z','Y');
                      for($i=8;$i>=1;$i--)
                          if(bccomp($byte,bcpow($base, $i))>=0)
                              return bcdiv($byte,bcpow($base, $i), $precision).' '.$labels[$i-1].($mibi?'iB':'B');
                      return $byte.' Byte';
                  }
                  

                  【讨论】:

                  • 只是一点旁注;如果$base$i 不是字符串值,bcpow() 将抛出 TypeError 异常。在 PHP 版本 7.0.11 上测试。
                  • 谢谢!我添加了字符串脚轮并修复了一个偏移错误:)
                  【解决方案13】:

                  我知道现在回答这个问题可能有点晚了,但是更多的数据不会害死人。这是一个非常快速的函数:

                  function format_filesize($B, $D=2){
                      $S = 'BkMGTPEZY';
                      $F = floor((strlen($B) - 1) / 3);
                      return sprintf("%.{$D}f", $B/pow(1024, $F)).' '.@$S[$F].'B';
                  }
                  

                  编辑:我更新了我的帖子以包含 camomileCase 提出的修复:

                  function format_filesize($B, $D=2){
                      $S = 'kMGTPEZY';
                      $F = floor((strlen($B) - 1) / 3);
                      return sprintf("%.{$D}f", $B/pow(1024, $F)).' '.@$S[$F-1].'B';
                  }
                  

                  【讨论】:

                  • 对于较小的 $B 值,您会得到一个双 B (BB),作为一种变通方法,您可以使用 "$S = 'kMGTPEZY'",而不是 "@$S[$F] " 做 "@$S[$F-1]"。
                  • @camomileCase 两年半后 - 我更新了我的答案。谢谢。
                  【解决方案14】:

                  这是 Chris Jester-Young 的实现,我见过的最干净的实现,结合了 php.net 和一个精度参数。

                  function formatBytes($size, $precision = 2)
                  {
                      $base = log($size, 1024);
                      $suffixes = array('', 'K', 'M', 'G', 'T');   
                  
                      return round(pow(1024, $base - floor($base)), $precision) .' '. $suffixes[floor($base)];
                  }
                  
                  echo formatBytes(24962496);
                  // 23.81M
                  
                  echo formatBytes(24962496, 0);
                  // 24M
                  
                  echo formatBytes(24962496, 4);
                  // 23.8061M
                  

                  【讨论】:

                  • 它有 2 个错误 - 将 1 添加到(至少小)文件大小 - 不使用 0(返回 NAN)
                  • 不错的一个。有相反的版本吗?
                  • 一个小梦想 $suffixes = array('B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');我想要一个Yottabyte硬盘! :-P
                  • 我必须将 $size 转换为 double 才能使其工作。这对我有用:函数 formatBytes($size, $precision = 2){ $base = log(floatval($size)) / log(1024); $suffixes = array('', 'k', 'M', 'G', 'T');返回 round(pow(1024, $base - floor($base)), $precision) 。 $suffixes[地板($base)]; }
                  • formatBytes(259748192, 3) 返回259748192 MB 这是不对的
                  【解决方案15】:

                  有点晚了,但接受的答案稍快的版本如下:

                  function formatBytes($bytes, $precision)
                  {
                      $unit_list = array
                      (
                          'B',
                          'KB',
                          'MB',
                          'GB',
                          'TB',
                      );
                  
                      $bytes = max($bytes, 0);
                      $index = floor(log($bytes, 2) / 10);
                      $index = min($index, count($unit_list) - 1);
                      $bytes /= pow(1024, $index);
                  
                      return round($bytes, $precision) . ' ' . $unit_list[$index];
                  }
                  

                  由于执行单个 log-2 操作而不是两个 log-e 操作,因此效率更高。

                  然而,执行下面更明显的解决方案实际上更快:

                  function formatBytes($bytes, $precision)
                  {
                      $unit_list = array
                      (
                          'B',
                          'KB',
                          'MB',
                          'GB',
                          'TB',
                      );
                  
                      $index_max = count($unit_list) - 1;
                      $bytes = max($bytes, 0);
                  
                      for ($index = 0; $bytes >= 1024 && $index < $index_max; $index++)
                      {
                          $bytes /= 1024;
                      }
                  
                      return round($bytes, $precision) . ' ' . $unit_list[$index];
                  }
                  

                  这是因为作为索引同时计算的值以适当的字节数为单位。这将执行时间减少了大约 35%(速度提高了 55%)。

                  【讨论】:

                    【解决方案16】:
                    function changeType($size, $type, $end){
                        $arr = ['B', 'KB', 'MB', 'GB', 'TB'];
                        $tSayi = array_search($type, $arr);
                        $eSayi = array_search($end, $arr);
                        $pow = $eSayi - $tSayi;
                        return $size * pow(1024 * $pow) . ' ' . $end;
                    }
                    
                    echo changeType(500, 'B', 'KB');
                    

                    【讨论】:

                    • 这个答案缺少教育解释。
                    【解决方案17】:

                    这里是 Drupal format_size 函数的简化实现:

                    /**
                     * Generates a string representation for the given byte count.
                     *
                     * @param $size
                     *   A size in bytes.
                     *
                     * @return
                     *   A string representation of the size.
                     */
                    function format_size($size) {
                      if ($size < 1024) {
                        return $size . ' B';
                      }
                      else {
                        $size = $size / 1024;
                        $units = ['KB', 'MB', 'GB', 'TB'];
                        foreach ($units as $unit) {
                          if (round($size, 2) >= 1024) {
                            $size = $size / 1024;
                          }
                          else {
                            break;
                          }
                        }
                        return round($size, 2) . ' ' . $unit;
                      }
                    }
                    

                    【讨论】:

                      【解决方案18】:
                      function byte_format($size) {
                          $bytes = array( ' KB', ' MB', ' GB', ' TB' );
                          foreach ($bytes as $val) {
                              if (1024 <= $size) {
                                  $size = $size / 1024;
                                  continue;
                              }
                              break;
                          }
                          return round( $size, 1 ) . $val;
                      }
                      

                      【讨论】:

                        【解决方案19】:

                        我的方法

                            function file_format_size($bytes, $decimals = 2) {
                          $unit_list = array('B', 'KB', 'MB', 'GB', 'PB');
                        
                          if ($bytes == 0) {
                            return $bytes . ' ' . $unit_list[0];
                          }
                        
                          $unit_count = count($unit_list);
                          for ($i = $unit_count - 1; $i >= 0; $i--) {
                            $power = $i * 10;
                            if (($bytes >> $power) >= 1)
                              return round($bytes / (1 << $power), $decimals) . ' ' . $unit_list[$i];
                          }
                        }
                        

                        【讨论】:

                        【解决方案20】:

                        只是我的选择,简短而干净:

                        /**
                         * @param int $bytes Number of bytes (eg. 25907)
                         * @param int $precision [optional] Number of digits after the decimal point (eg. 1)
                         * @return string Value converted with unit (eg. 25.3KB)
                         */
                        function formatBytes($bytes, $precision = 2) {
                            $unit = ["B", "KB", "MB", "GB"];
                            $exp = floor(log($bytes, 1024)) | 0;
                            return round($bytes / (pow(1024, $exp)), $precision).$unit[$exp];
                        }
                        

                        或者,更愚蠢更高效:

                        function formatBytes($bytes, $precision = 2) {
                            if ($bytes > pow(1024,3)) return round($bytes / pow(1024,3), $precision)."GB";
                            else if ($bytes > pow(1024,2)) return round($bytes / pow(1024,2), $precision)."MB";
                            else if ($bytes > 1024) return round($bytes / 1024, $precision)."KB";
                            else return ($bytes)."B";
                        }
                        

                        【讨论】:

                          【解决方案21】:

                          试试这个;)

                          function bytesToSize($bytes) {
                                          $sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
                                          if ($bytes == 0) return 'n/a';
                                          $i = intval(floor(log($bytes) / log(1024)));
                                          if ($i == 0) return $bytes . ' ' . $sizes[$i]; 
                                          return round(($bytes / pow(1024, $i)),1,PHP_ROUND_HALF_UP). ' ' . $sizes[$i];
                                      }
                          echo bytesToSize(10000050300);
                          

                          【讨论】:

                          • 这个答案缺少教育解释。
                          【解决方案22】:

                          我成功完成了以下功能,

                              function format_size($size) {
                                  $mod = 1024;
                                  $units = explode(' ','B KB MB GB TB PB');
                                  for ($i = 0; $size > $mod; $i++) {
                                      $size /= $mod;
                                  }
                                  return round($size, 2) . ' ' . $units[$i];
                              }
                          

                          【讨论】:

                          • 注意:K 代表开尔文,k 代表公斤。
                          【解决方案23】:

                          如果您需要短代码,请使用此功能

                          bcdiv()

                          $size = 11485760;
                          echo bcdiv($size, 1048576, 0); // return: 10
                          
                          echo bcdiv($size, 1048576, 2); // return: 10,9
                          
                          echo bcdiv($size, 1048576, 2); // return: 10,95
                          
                          echo bcdiv($size, 1048576, 3); // return: 10,953
                          

                          【讨论】:

                          • 第二个 bcdiv 的第三个参数应该是 1,而不是 2。
                          【解决方案24】:
                          function formatBytes($bytes, $precision = 2) { 
                              $units = array('B', 'KB', 'MB', 'GB', 'TB'); 
                          
                              $bytes = max($bytes, 0); 
                              $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); 
                              $pow = min($pow, count($units) - 1); 
                          
                              // Uncomment one of the following alternatives
                              // $bytes /= pow(1024, $pow);
                              // $bytes /= (1 << (10 * $pow)); 
                          
                              return round($bytes, $precision) . ' ' . $units[$pow]; 
                          } 
                          

                          (取自php.net,还有很多其他的例子,但我最喜欢这个:-)

                          【讨论】:

                          • 如果您使用$bytes /= (1 &lt;&lt; (10 * $pow)) 或类似的,我会更喜欢它。 :-P
                          • 你去吧:D(我个人不喜欢按位算术,因为如果你不习惯它就很难理解......)
                          • @Justin 那是因为 9287695 / 1024 / 1024 确实是 8,857 :)
                          • 其实是KiBMiBGiBTiB,因为你除以1024。如果你除以1000,那就没有i
                          • Uncomment one of the following alternatives 是我 5 分钟没有注意到的东西......
                          【解决方案25】:

                          伪代码:

                          $base = log($size) / log(1024);
                          $suffix = array("", "k", "M", "G", "T")[floor($base)];
                          return pow(1024, $base - floor($base)) . $suffix;
                          

                          【讨论】:

                          • 微软和苹果使用 1024,这取决于你的用例。
                          • 这看起来不像伪代码;还是您只是说它是“未经测试的代码”?此答案缺少教育解释。
                          【解决方案26】:

                          这是Kohana's 实现,你可以使用它:

                          public static function bytes($bytes, $force_unit = NULL, $format = NULL, $si = TRUE)
                          {
                              // Format string
                              $format = ($format === NULL) ? '%01.2f %s' : (string) $format;
                          
                              // IEC prefixes (binary)
                              if ($si == FALSE OR strpos($force_unit, 'i') !== FALSE)
                              {
                                  $units = array('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB');
                                  $mod   = 1024;
                              }
                              // SI prefixes (decimal)
                              else
                              {
                                  $units = array('B', 'kB', 'MB', 'GB', 'TB', 'PB');
                                  $mod   = 1000;
                              }
                          
                              // Determine unit to use
                              if (($power = array_search((string) $force_unit, $units)) === FALSE)
                              {
                                  $power = ($bytes > 0) ? floor(log($bytes, $mod)) : 0;
                              }
                          
                              return sprintf($format, $bytes / pow($mod, $power), $units[$power]);
                          }
                          

                          【讨论】:

                          • 他们在 1024 和 1000 功率之间选择的想法很好。但是这个实现真的很奇怪。 $force_unit$si 似乎做同样的事情。您还可以将任何带有“i”的字符串传递给$force_unit,因为它们会测试位置。十进制格式也太过分了。
                          【解决方案27】:

                          只需将其除以 1024(kb)、1024^2(mb)和 1024^3(GB)。就这么简单。

                          【讨论】: