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?

leepowers
la source
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 :-)

Leo
la source
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
John Himmelman
la source
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;
Chris Jester-Young
la source
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.

Vonder
la source
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]);
}
Ryeguy
la source
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";
}
guari
la source
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
Yanni
la source
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';
}
David Bélanger
la source
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
SebHallin
la source
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"
Pavel Tzonkov
la source
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];
    }
Janith Chinthana
la source
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];
  }
}
user24087
la source
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];
}
ZettiCaletti
la source
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;
}
Gabriel Barcellos
la source
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);
Yahia Mgarrech
la source
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');
Kerem Çakır
la source
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'
Madushanka Sampath
la source
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]; 
} 
Josué Leo Moreno
la source
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.

Ben Johnson
la source
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;
}
mooky
la source
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;
  }
}
ya.teck
la source
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%).

user3690595
la source
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';
}
Christian
la source
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!

EML
la source
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];
}
?>
John Erck
la source
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 ....".

Juan Lago
la source
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];
}
Aile de Steely
la source