This snippet is a modified version of the first posting which has been refactored by Ben Laan (see comments below). The original script was copied from some other website quite a while ago, cannot remember where from, but I have used it extensively and thought I would share this useful method. If you know who the original author is please let me know.
public string FormatBytes(long bytes)
{
const int scale = 1024;
string[] orders = new string[] { "GB", "MB", "KB", "Bytes" };
long max = (long)Math.Pow(scale, orders.Length - 1);
foreach (string order in orders)
{
if ( bytes > max )
return string.Format("{0:##.##} {1}", decimal.Divide( bytes, max ), order);
max /= scale;
}
return "0 Bytes";
}
The original code:
public string FormatBytes(int Bytes)
{
string filesize;
if (Bytes >= 1073741824)
{
decimal size = decimal.Divide(Bytes, 1073741824);
filesize = string.Format("{0:##.##} GB", size);
}
else if (Bytes >= 1048576)
{
decimal size = decimal.Divide(Bytes, 1048576);
filesize = string.Format("{0:##.##} MB", size);
}
else if (Bytes >= 1024)
{
decimal size = decimal.Divide(Bytes, 1024);
filesize = string.Format("{0:##.##} KB", size);
}
else if (Bytes > 0 & Bytes < 1024)
{
decimal size = Bytes;
filesize = string.Format("{0:##.##} Bytes", size);
}
else
{
filesize = "0 Bytes";
}
return filesize;
}
I also have a similar function written in PHP to pretty format file sizes.
I think the newer solution is slower and incorrect.
[Fact]
public void ShouldFormatGigabytes()
{
const long sut = 1 * 1024 * 1024 * 1024;
Assert.Equal("1 GB", FormatBytes(sut));
}
Actual: 1024 MB
Another refactoring without the loop (in VB):
Private Function FormatBytes(ByVal bytes As Long) As String
Const scale = 1024
Dim orders As String() = New String() {"", "K", "M", "G", "T", "P", "E"}
Dim rank = Math.Floor(Math.Log(bytes, scale))
If rank >= orders.Length Then rank -= 1
Return String.Format("{0:##.##} {1}B", bytes / Math.Pow(scale, rank), orders(rank))
End Function
if (bytes == max) return string.Format("1 {0}", order);
I got the ultimate solution:
return (String.Format("{0:##.##0}",
Bytes >= 1073741824 ? decimal.Divide(Bytes, 1073741824) + "GB" :
Bytes >= 1048576 ? decimal.Divide(Bytes, 1048576) + "MB" :
Bytes >= 1024 ? decimal.Divide(Bytes, 1024) + "KB" :
Bytes > 0 & Bytes < 1024 ? Bytes + "Bytes" : 0 + "0 Bytes"
));
Happy formating
So I used this in a console app recently as well, here was my function.. just to wrap it up..
static string FormatBytes(long bytes)
{
const long scale = 1024;
string[] orders = new string[]{ "YB", "ZB", "EB", "PB", "TB", "GB", "MB", "KB", "Bytes" };
var max = (long)Math.Pow(scale, (orders.Length - 1));
foreach (string order in orders)
{
if (bytes > max)
return string.Format("{0:##.##} {1}", Decimal.Divide(bytes, max), order);
max /= scale;
}
return "0 Bytes";
}
Okay so this actually only works up to the EB's, it kind of looks like somewhere between EB's and ZB's we hit the max value for the long data type, and the previous function would return just "YB" for all of the string values. So I modified the orders array to this:
string[] orders = new string[]{ "EB", "PB", "TB", "GB", "MB", "KB", "Bytes" };
You could try a different data type, but I'm not measuring anything larger than EB's anyways.
Hi, nice code, thanks!
I also changed this line to do a little future proofing for Diomede Storage.
string[] orders = new string[] { "YB", "ZB", "EB", "PB", "TB", "GB", "MB", "KB", "Bytes" };
-- Ross
@Ross what about Brontobytes?
thanks code worked great. if your bytes are displayed in MB like mine were all you have to do is modify the scale..mine looked something like scale = (1024/10) or you could go the other way scale = 10240
awesome code thanks a bunch.
Hi ben,
Thanks for the refactor!
I wasn't completely happy with the code, especially the hard coded byte values; however I copied if from the somewhere on the web and just carried on using it. "If it ain't broke, don't fix it".
I will probably give your version a try though!
Thanks
This code has 4 repeating blocks! Sounds like it needs a good refactoring:
public string FormatBytesNew( long bytes )
{
const int scale = 1024;
var orders = new[] { "GB", "MB", "KB", "Bytes" };
var max = (long) Math.Pow( scale, orders.Length - 1 );
foreach ( var order in orders )
{
if ( bytes > max )
return string.Format( "{0:##.##} {1}", Decimal.Divide( bytes, max ), order );
max /= scale;
}
return "0 Bytes";
}