Title: Format file sizes in KB, MB, GB, and so forth in C#
This example shows how to format file sizes in KB, MB, GB, and so forth. It defines two extension methods that convert numeric values into strings representing a number of bytes in bytes, KB, MB, and so forth. The first extension method uses an API function. The second method uses only C# code.
Using an API Function
The ToFileSizeApi extension method applies to the long class. It starts by including the following using statement:
using System.Runtime.InteropServices;
The SizeExtensions class also includes the following declaration of the StrFormatByteSize API function:
[DllImport("Shlwapi.dll", CharSet = CharSet.Auto)]
public static extern Int32 StrFormatByteSize(
long fileSize,
[MarshalAs(UnmanagedType.LPTStr)] StringBuilder buffer,
int bufferSize);
This API function returns a string similar to the one that Windows uses when it needs to display the size of a file. For example, it should give results similar to those displayed by Windows Explorer.
The following code shows the ToFileSizeApi extension method.
// Return a file size created by the StrFormatByteSize API function.
public static string ToFileSizeApi(this long file_size)
{
StringBuilder sb = new StringBuilder(20);
StrFormatByteSize(file_size, sb, 20);
return sb.ToString();
}
Using C# Code
The StrFormatByteSize API function has one tiny drawback: it only works with sizes up to petabytes. Admittedly this is a lot (1 petabyte = 1,000 terabytes), but some day you might want to display values that are even larger.
To handle larger file sizes, the SizeExtensions class also provides an extension method that converts doubles into file size strings, and it can handle values up to yottabytes (1,0008 bytes or 1 trillion terabytes).
(As of 2011, the sum of the storage on every computer in the world was estimated at around 295 exabytes or 295 billion gigabytes, so this should be enough for a while, at least if you're measuring real file sizes.)
The following code shows the ToFileSize extension method.
// Return a string describing the value as a file size.
// For example, 1.23 MB.
public static string ToFileSize(this double value)
{
string[] suffixes = { "bytes", "KB", "MB", "GB",
"TB", "PB", "EB", "ZB", "YB"};
for (int i = 0; i < suffixes.Length; i++)
{
if (value <= (Math.Pow(1024, i + 1)))
{
return ThreeNonZeroDigits(value /
Math.Pow(1024, i)) +
" " + suffixes[i];
}
}
return ThreeNonZeroDigits(value /
Math.Pow(1024, suffixes.Length - 1)) +
" " + suffixes[suffixes.Length - 1];
}
This method compares the value to larger and larger powers of 1024. When it finds a power of 1024 greater than or equal to the value, the code divides the value by the next smaller power and adds the appropriate suffix.
For example, suppose the number is 10,000,000. This values is between 10242 = 1,048,576 and 10243 = 1,073,741,824. So the program divides the value by 10242 to get 10,000,000 ÷ 10242 ≈ 9.5361328125. It passes that value and the corresponding suffix MB to the following ThreeNonZeroDigits method.
// Return the value formatted to include at most three
// non-zero digits and at most two digits after the
// decimal point. Examples:
// 1
// 123
// 12.3
// 1.23
// 0.12
private static string ThreeNonZeroDigits(double value)
{
if (value >= 100)
{
// No digits after the decimal.
return value.ToString("0,0");
}
else if (value >= 10)
{
// One digit after the decimal.
return value.ToString("0.0");
}
else
{
// Two digits after the decimal.
return value.ToString("0.00");
}
}
The ThreeNonZeroDigits method returns up to three non-zero digits for a number.
If the value is at least 100, then it needs three digits anyway so the method simply returns the value formatted as a string with no values after the decimal point.
If the value is at least 10 but less than 100, the method returns the value formatted as a string with two digits before the decimal point and one after.
Finally if the value is less than 10, the method returns the value formatted as a string with one digit before the decimal point and two after.
Using the Methods
Using the extension methods is easy. If size is a long, then the following code calls the two extension methods to display the value as a file size.
long long_size = 1234567890;
double double_size = 1234567890;
Console.WriteLine(long_size.ToFileSizeApi());
Console.WriteLine(double_size.ToFileSize());
Notes
Note that there is some ambiguity here. Some people define a kilobyte as 1024 bytes but others (particularly disk drive manufacturers who like to make you think your drive is bigger than it really is) define it as 1000 bytes.
The ToFileSize method assumes a kilobyte is 1024 bytes. You can easily change this if you want to work with 1000 byte kilobytes.
Note also that the StrFormatByteSize API function doesn't return the same result as ToFileSize. For some reason, the API function seems to be truncating. For example, 12,345,000 is approximately 11.77 MB but StrFormatByteSize returns 11.7 MB instead of 11.8 MB.
The example program displays several numbers with their file size strings using both of these extension methods so you can compare the results.
Download the example to experiment with it and to see additional details.
|