Scenario: the size of various files are stored in a database as bytes. What's the best way to format this size info to kilobytes, megabytes and gigabytes? For instance I have an MP3 that Ubuntu displays as "5.2 MB (5445632 bytes)". How would I display this on a web page as "5.2 MB" AND have files less than one megabyte display as KB and files one gigabyte and above display as GB?
This question is related to
php
I don't know why you should make it so complicated as the others.
The following code is much simpler to understand and about 25% faster than the other solutions who uses the log function (called the function 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];
}
My approach
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];
}
}
I know it's maybe a little late to answer this question but, more data is not going to kill someone. Here's a very fast function :
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: I updated my post to include the fix proposed by 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';
}
Flexible solution:
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"
Albeit a bit stale, this library offers a tested and robust conversion API:
https://github.com/gabrielelana/byte-units
Once installed:
\ByteUnits\Binary::bytes(1024)->format();
// Output: "1.00KiB"
And to convert in the other direction:
\ByteUnits\Binary::parse('1KiB')->numberOfBytes();
// Output: "1024"
Beyond basic conversion, it offers methods for addition, subtraction, comparison, etc.
I am no way affiliated with this library.
Just divide it by 1024 for kb, 1024^2 for mb and 1024^3 for GB. As simple as that.
This work with the last 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];
}
Extremely simple function to get human file size.
Original source: http://php.net/manual/de/function.filesize.php#106569
Copy/paste 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];
}
?>
I succeeded with following function,
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];
}
This is Chris Jester-Young's implementation, cleanest I've ever seen, combined with php.net's and a precision argument.
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
Base on Leo's answer, add
If you want max unit to Mega, change to $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];
}
Pseudocode:
$base = log($size) / log(1024);
$suffix = array("", "k", "M", "G", "T")[floor($base)];
return pow(1024, $base - floor($base)) . $suffix;
I developed my own function that convert human readable memory size to different sizes.
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
This function accepts any memory size abbreviation like "Megabyte, MB, Mb, mb, m, kilobyte, K, KB, b, Terabyte, T...." so it is typo safe.
Just my alternative, short and clean:
/**
* @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];
}
or, more stupid and efficent:
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";
}
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];
}
Just call the function
echo convertToReadableSize(1024); // Outputs '1KB'
echo convertToReadableSize(1024 * 1024); // Outputs '1MB'
use this function if you want a short code
$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
This is Kohana's implementation, you could use it:
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]);
}
Another condensed implementation which can translate to the base 1024 (binary) or base 1000 (decimal) and also works with incredibly large numbers hence of the use of the bc library:
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';
}
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');
Here is simplified implementation of the Drupal format_size function:
/**
* 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;
}
}
Simple function
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
I did this converting all input to byte and so converting to any output needed. Also, I used a auxiliar function to get base 1000 or 1024, but left it flex to decide use 1024 on popular type (without 'i', like MB instead of 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;
}
try this ;)
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);
It's a little late but a slightly faster version of the accepted answer is below:
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];
}
It's more efficient, due to performing a single log-2 operation instead of two log-e operations.
It's actually faster to do the more obvious solution below, however:
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];
}
This is because as the index is calculated at the same time as the value of the number of bytes in the appropriate unit. This cut the execution time by about 35% (a 55% speed increase).
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;
}
I figured I would add a meshing of two submitters code (Using John Himmelman's code, which is in this thread, and using Eugene Kuzmenko's code) that I'm using.
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
}
}
This uses Eugene's code to format the $value
into bytes (I keep my data in MB, so it converts my data: 10485760 MB
into 10995116277760
) - it then uses John's code to convert it into the proper display value (10995116277760
into 10 TB
).
I've found this really helpful - so my thanks to the two submitters!
Here is an option using log10
:
<?php
function format_number(float $n): string {
$n2 = (int)(log10($n) / 3);
return sprintf('%.3f', $n / 1e3 ** $n2) . ['', ' k', ' M', ' G'][$n2];
}
$s = format_number(9012345678);
var_dump($s == '9.012 G');
Source: Stackoverflow.com