Formater les octets en kilo-octets, mégaoctets, gigaoctets


187

Scénario: la taille des différents fichiers est stockée dans une base de données sous forme d'octets. Quelle est la meilleure façon de formater ces informations de taille en kilo-octets, mégaoctets et gigaoctets? Par exemple, j'ai un MP3 qu'Ubuntu affiche comme "5,2 Mo (5445632 octets)". Comment est-ce que j'afficherais ceci sur une page Web en tant que «5,2 Mo» ET que les fichiers de moins d'un mégaoctet s'affichent en Ko et les fichiers d'un gigaoctet et plus s'affichent en Go?


3
Je crois que vous devriez créer une fonction faisant cela. Divisez simplement le nombre par 1024 et regardez le résultat. Si c'est plus de 1024 alors divisez à nouveau.
Ivan Nevostruev

Réponses:


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

(Tiré de php.net , il y a beaucoup d'autres exemples, mais j'aime mieux celui-ci :-)


8
Si vous avez utilisé $bytes /= (1 << (10 * $pow))ou similaire, je pourrais mieux l'aimer. :-P
Chris Jester-Young

5
Voilà: D (personnellement, je n'aime pas l'arithmétique au niveau du bit, car c'est difficile à comprendre si vous n'y êtes pas habitué ...)
Leo

3
@Justin c'est parce que 9287695/1024/1024 est en effet 8857 :)
Mahn

30
En fait, il est KiB, MiB, GiBet TiBpuisque vous divisez par 1024. Si vous divisez par 1000elle, ce serait sans le i.
Devator

8
Uncomment one of the following alternativesétait quelque chose que je n'ai pas remarqué pendant 5 minutes ...
Arnis Juraga

212

C'est l'implémentation de Chris Jester-Young, la plus propre que j'aie jamais vue, combinée avec php.net et un argument de précision.

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

8
il a 2 erreurs - ajoutez 1 à la taille (au moins petite) des fichiers - ne fonctionne pas avec 0 (retourne NAN)
maazza

Joli. Y a-t-il une version de ceci dans l'autre sens?
Luke

3
un petit rêve : $suffixes = array('B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'); je veux un disque dur de Yottabyte! :-P
SpYk3HH

1
J'ai dû multiplier par deux la taille de $ pour que cela fonctionne. voici ce qui a fonctionné pour moi: function formatBytes ($ size, $ precision = 2) {$ base = log (floatval ($ size)) / log (1024); $ suffixes = tableau ('', 'k', 'M', 'G', 'T'); return round (pow (1024, $ base - floor ($ base)), $ precision). $ suffixes [floor ($ base)]; }
Christopher Gray

formatBytes(259748192, 3)renvoie 259748192 MBce qui n'est pas correct
Retourner le

97

Pseudocode:

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

Microsoft et Apple utilisent 1024, cela dépend de votre cas d'utilisation.
Parsa Yazdani

15

Divisez-le simplement par 1024 pour kb, 1024 ^ 2 pour mb et 1024 ^ 3 pour Go. Aussi simple que cela.


15

C'est l' implémentation de Kohana , vous pouvez l'utiliser:

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

Leur idée d'avoir une option entre 1024 et 1000 puissance est bonne. Mais cette implémentation est vraiment bizarre. $force_unitet $sisemblent faire la même chose. Vous pouvez également transmettre n'importe quelle chaîne contenant un "i" à $force_unit, car ils testent la position. Le formatage décimal est également exagéré.
Gus Neves

8

Juste mon alternative, courte et propre:

/**
 * @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];
}

ou, plus stupide et efficace:

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

7

utilisez cette fonction si vous voulez un code court

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

6

Je sais qu'il est peut-être un peu tard pour répondre à cette question, mais plus de données ne tueront personne. Voici une fonction très rapide:

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

EDIT: J'ai mis à jour mon message pour inclure le correctif proposé par 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';
}

1
Vous obtenez un double B (BB) pour les petites valeurs de $ B, car une solution de contournement pourrait faire "$ S = 'kMGTPEZY'", et au lieu de "@ $ S [$ F]" do "@ $ S [$ F-1] ".
camomileCase

@camomileCase Deux ans et demi plus tard - j'ai mis à jour ma réponse. Merci.
David Bélanger

4

Fonction simple

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

3

Solution flexible:

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"

2

J'ai réussi avec la fonction suivante,

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

2
Attention: K est pour Kelvin et k est pour kilos.
ZeWaren

2

Mon approche

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

2

Je ne sais pas pourquoi vous devriez le rendre aussi compliqué que les autres.

Le code suivant est beaucoup plus simple à comprendre et environ 25% plus rapide que les autres solutions qui utilisent la fonction log (appelée la fonction 20 millions de fois avec des paramètres différents)

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

2

J'ai fait cela en convertissant toutes les entrées en octets et en les convertissant ainsi en toute sortie nécessaire. De plus, j'ai utilisé une fonction auxiliaire pour obtenir la base 1000 ou 1024, mais je l'ai laissée fléchir pour décider d'utiliser 1024 sur le type populaire (sans «i», comme MB au lieu de 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;
}

1

essaye ça ;)

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

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

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

Appelez simplement la fonction

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

1

Ce travail avec le dernier PHP

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

    $bytes /= pow(1024, $pow); 

    return round($bytes, $precision) . ' ' . $units[$pow]; 
} 

2
Tout ce qui a été fait, c'est la même copie exacte d'un exemple de PHP.net qui a été faite par le répondeur principal en 2010, ne le faisant que 8 ans plus tard.
Giacomo1968

1

Bien qu'un peu obsolète, cette bibliothèque propose une API de conversion testée et robuste:

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

Une fois installé:

\ByteUnits\Binary::bytes(1024)->format();

// Output: "1.00KiB"

Et pour convertir dans l'autre sens:

\ByteUnits\Binary::parse('1KiB')->numberOfBytes();

// Output: "1024"

Au-delà de la conversion de base, il propose des méthodes d'addition, de soustraction, de comparaison, etc.

Je ne suis aucunement affilié à cette bibliothèque.


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

0

Voici une implémentation simplifiée de la fonction 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;
  }
}

0

Il est un peu tard mais une version légèrement plus rapide de la réponse acceptée est ci-dessous:

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

C'est plus efficace, car il effectue une seule opération log-2 au lieu de deux opérations log-e.

Cependant, il est en fait plus rapide de faire la solution la plus évidente ci-dessous:

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

En effet, comme l'index est calculé en même temps que la valeur du nombre d'octets dans l'unité appropriée. Cela a réduit le temps d'exécution d'environ 35% (une augmentation de vitesse de 55%).


0

Une autre implémentation condensée qui peut se traduire en base 1024 (binaire) ou en base 1000 (décimal) et fonctionne également avec des nombres incroyablement grands d'où l'utilisation de la bibliothèque 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';
}

Juste une petite note latérale; bcpow()lancera une exception TypeError si $baseet $ine sont pas des valeurs de chaîne. Testé sur PHP version 7.0.11.
David Cery

Merci! J'ai ajouté le lanceur de cordes et corrigé un bug de décalage :)
Christian

0

J'ai pensé que j'ajouterais un maillage de code de deux soumetteurs (en utilisant le code de John Himmelman, qui est dans ce fil, et en utilisant le code d' Eugene Kuzmenko ) que j'utilise.

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

Cela utilise le code d'Eugene pour formater le $valueen octets (je garde mes données en Mo, donc il convertit mes données: 10485760 MBen 10995116277760) - il utilise ensuite le code de John pour le convertir en la valeur d'affichage appropriée ( 10995116277760en 10 TB).

J'ai trouvé cela vraiment utile - donc mes remerciements aux deux soumissionnaires!


0

Fonction extrêmement simple pour obtenir la taille du fichier humain.

Source originale: http://php.net/manual/de/function.filesize.php#106569

Copier / coller du code:

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

0

J'ai développé ma propre fonction qui convertit la taille de la mémoire lisible par l'homme en différentes tailles.

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

Cette fonction accepte toutes les abréviations de taille de mémoire telles que "Mégaoctet, Mo, Mb, mb, m, kilo-octet, K, KB, b, Téraoctet, T ....".


0

Basez-vous sur la réponse de Leo , ajoutez

  • Prise en charge du négatif
  • Prise en charge 0 <valeur <1 (Ex: 0,2, provoquera log (valeur) = nombre négatif)

Si vous voulez que l'unité maximale soit Mega, passez à $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];
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.