[ACCEPTED]-File-size format provider-filesize
I use this one, I get it from the web
public class FileSizeFormatProvider : IFormatProvider, ICustomFormatter
{
public object GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter)) return this;
return null;
}
private const string fileSizeFormat = "fs";
private const Decimal OneKiloByte = 1024M;
private const Decimal OneMegaByte = OneKiloByte * 1024M;
private const Decimal OneGigaByte = OneMegaByte * 1024M;
public string Format(string format, object arg, IFormatProvider formatProvider)
{
if (format == null || !format.StartsWith(fileSizeFormat))
{
return defaultFormat(format, arg, formatProvider);
}
if (arg is string)
{
return defaultFormat(format, arg, formatProvider);
}
Decimal size;
try
{
size = Convert.ToDecimal(arg);
}
catch (InvalidCastException)
{
return defaultFormat(format, arg, formatProvider);
}
string suffix;
if (size > OneGigaByte)
{
size /= OneGigaByte;
suffix = "GB";
}
else if (size > OneMegaByte)
{
size /= OneMegaByte;
suffix = "MB";
}
else if (size > OneKiloByte)
{
size /= OneKiloByte;
suffix = "kB";
}
else
{
suffix = " B";
}
string precision = format.Substring(2);
if (String.IsNullOrEmpty(precision)) precision = "2";
return String.Format("{0:N" + precision + "}{1}", size, suffix);
}
private static string defaultFormat(string format, object arg, IFormatProvider formatProvider)
{
IFormattable formattableArg = arg as IFormattable;
if (formattableArg != null)
{
return formattableArg.ToString(format, formatProvider);
}
return arg.ToString();
}
}
an 8 example of use would be:
Console.WriteLine(String.Format(new FileSizeFormatProvider(), "File size: {0:fs}", 100));
Console.WriteLine(String.Format(new FileSizeFormatProvider(), "File size: {0:fs}", 10000));
Credits for http://flimflan.com/blog/FileSizeFormatProvider.aspx
There 7 is a problem with ToString(), it's expecting 6 a NumberFormatInfo type that implements 5 IFormatProvider but the NumberFormatInfo 4 class is sealed :(
If you're using C# 3.0 3 you can use an extension method to get the 2 result you want:
public static class ExtensionMethods
{
public static string ToFileSize(this long l)
{
return String.Format(new FileSizeFormatProvider(), "{0:fs}", l);
}
}
You can use it like this.
long l = 100000000;
Console.WriteLine(l.ToFileSize());
Hope 1 this helps.
OK I'm not going to wrap it up as a Format 7 provider but rather than reinventing the 6 wheel there's a Win32 api call to format 5 a size string based on supplied bytes that 4 I've used many times in various applications.
[DllImport("Shlwapi.dll", CharSet = CharSet.Auto)]
public static extern long StrFormatByteSize( long fileSize, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder buffer, int bufferSize );
So 3 I imagine you should be able to put together 2 a provider using that as the core conversion 1 code.
Here's a link to the MSDN spec for StrFormatByteSize.
I realize now that you were actually asking 34 for something that would work with String.Format() - I 33 guess I should have read the question twice 32 before posting ;-)
I don't like the solution 31 where you have to explicitly pass in a format 30 provider every time - from what I could 29 gather from this article, the best way to approach this, is 28 to implement a FileSize type, implementing 27 the IFormattable interface.
I went ahead 26 and implemented a struct that supports this 25 interface, and which can be cast from an 24 integer. In my own file-related APIs, I 23 will have my .FileSize properties return 22 a FileSize instance.
Here's the code:
using System.Globalization;
public struct FileSize : IFormattable
{
private ulong _value;
private const int DEFAULT_PRECISION = 2;
private static IList<string> Units;
static FileSize()
{
Units = new List<string>(){
"B", "KB", "MB", "GB", "TB"
};
}
public FileSize(ulong value)
{
_value = value;
}
public static explicit operator FileSize(ulong value)
{
return new FileSize(value);
}
override public string ToString()
{
return ToString(null, null);
}
public string ToString(string format)
{
return ToString(format, null);
}
public string ToString(string format, IFormatProvider formatProvider)
{
int precision;
if (String.IsNullOrEmpty(format))
return ToString(DEFAULT_PRECISION);
else if (int.TryParse(format, out precision))
return ToString(precision);
else
return _value.ToString(format, formatProvider);
}
/// <summary>
/// Formats the FileSize using the given number of decimals.
/// </summary>
public string ToString(int precision)
{
double pow = Math.Floor((_value > 0 ? Math.Log(_value) : 0) / Math.Log(1024));
pow = Math.Min(pow, Units.Count - 1);
double value = (double)_value / Math.Pow(1024, pow);
return value.ToString(pow == 0 ? "F0" : "F" + precision.ToString()) + " " + Units[(int)pow];
}
}
And 21 a simple Unit Test that demonstrates how 20 this works:
[Test]
public void CanUseFileSizeFormatProvider()
{
Assert.AreEqual(String.Format("{0}", (FileSize)128), "128 B");
Assert.AreEqual(String.Format("{0}", (FileSize)1024), "1.00 KB");
Assert.AreEqual(String.Format("{0:0}", (FileSize)10240), "10 KB");
Assert.AreEqual(String.Format("{0:1}", (FileSize)102400), "100.0 KB");
Assert.AreEqual(String.Format("{0}", (FileSize)1048576), "1.00 MB");
Assert.AreEqual(String.Format("{0:D}", (FileSize)123456), "123456");
// You can also manually invoke ToString(), optionally with the precision specified as an integer:
Assert.AreEqual(((FileSize)111111).ToString(2), "108.51 KB");
}
As you can see, the FileSize 19 type can now be formatted correctly, and 18 it is also possible to specify the number 17 of decimals, as well as applying regular 16 numeric formatting if required.
I guess you 15 could take this much further, for example 14 allowing explicit format selection, e.g. "{0:KB}" to 13 force formatting in kilobytes. But I'm going 12 to leave it at this.
I'm also leaving my 11 initial post below for those two prefer 10 not to use the formatting API...
100 ways 9 to skin a cat, but here's my approach - adding 8 an extension method to the int type:
public static class IntToBytesExtension
{
private const int PRECISION = 2;
private static IList<string> Units;
static IntToBytesExtension()
{
Units = new List<string>(){
"B", "KB", "MB", "GB", "TB"
};
}
/// <summary>
/// Formats the value as a filesize in bytes (KB, MB, etc.)
/// </summary>
/// <param name="bytes">This value.</param>
/// <returns>Filesize and quantifier formatted as a string.</returns>
public static string ToBytes(this int bytes)
{
double pow = Math.Floor((bytes>0 ? Math.Log(bytes) : 0) / Math.Log(1024));
pow = Math.Min(pow, Units.Count-1);
double value = (double)bytes / Math.Pow(1024, pow);
return value.ToString(pow==0 ? "F0" : "F" + PRECISION.ToString()) + " " + Units[(int)pow];
}
}
With 7 this extension in your assembly, to format 6 a filesize, simply use a statement like 5 (1234567).ToBytes()
The following MbUnit 4 test clarifies precisely what the output 3 looks like:
[Test]
public void CanFormatFileSizes()
{
Assert.AreEqual("128 B", (128).ToBytes());
Assert.AreEqual("1.00 KB", (1024).ToBytes());
Assert.AreEqual("10.00 KB", (10240).ToBytes());
Assert.AreEqual("100.00 KB", (102400).ToBytes());
Assert.AreEqual("1.00 MB", (1048576).ToBytes());
}
And you can easily change the 2 units and precision to whatever suits your 1 needs :-)
this is the simplest implementation I know 3 to format file sizes:
public string SizeText
{
get
{
var units = new[] { "B", "KB", "MB", "GB", "TB" };
var index = 0;
double size = Size;
while (size > 1024)
{
size /= 1024;
index++;
}
return string.Format("{0:2} {1}", size, units[index]);
}
}
Whereas Size is the 2 unformatted file size in bytes.
Greetings Christian
My code... thanks for Shaun Austin.
[DllImport("Shlwapi.dll", CharSet = CharSet.Auto)]
public static extern long StrFormatByteSize(long fileSize, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder buffer, int bufferSize);
public void getFileInfo(string filename)
{
System.IO.FileInfo fileinfo = new FileInfo(filename);
this.FileName.Text = fileinfo.Name;
StringBuilder buffer = new StringBuilder();
StrFormatByteSize(fileinfo.Length, buffer, 100);
this.FileSize.Text = buffer.ToString();
}
0
since shifting is a very cheap operation
public static string ToFileSize(this long size)
{
if (size < 1024)
{
return (size).ToString("F0") + " bytes";
}
else if ((size >> 10) < 1024)
{
return (size/(float)1024).ToString("F1") + " KB";
}
else if ((size >> 20) < 1024)
{
return ((size >> 10) / (float)1024).ToString("F1") + " MB";
}
else if ((size >> 30) < 1024)
{
return ((size >> 20) / (float)1024).ToString("F1") + " GB";
}
else if ((size >> 40) < 1024)
{
return ((size >> 30) / (float)1024).ToString("F1") + " TB";
}
else if ((size >> 50) < 1024)
{
return ((size >> 40) / (float)1024).ToString("F1") + " PB";
}
else
{
return ((size >> 50) / (float)1024).ToString("F0") + " EB";
}
}
0
I needed a version that can be localized 4 for different cultures (decimal separator, "byte" translation) and 3 support for all possible binary prefixes (up to Exa). Here 2 is an example that demonstrates how to use 1 it:
// force "en-US" culture for tests
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(1033);
// Displays "8.00 EB"
Console.WriteLine(FormatFileSize(long.MaxValue));
// Use "fr-FR" culture. Displays "20,74 ko", o is for "octet"
Console.WriteLine(FormatFileSize(21234, "o", null, CultureInfo.GetCultureInfo(1036)));
And here is the code:
/// <summary>
/// Converts a numeric value into a string that represents the number expressed as a size value in bytes, kilobytes, megabytes, gigabytes, terabytes, petabytes or exabytes, depending on the size
/// </summary>
/// <param name="size">The size.</param>
/// <returns>
/// The number converted.
/// </returns>
public static string FormatFileSize(long size)
{
return FormatFileSize(size, null, null, null);
}
/// <summary>
/// Converts a numeric value into a string that represents the number expressed as a size value in bytes, kilobytes, megabytes, gigabytes, terabytes, petabytes or exabytes, depending on the size
/// </summary>
/// <param name="size">The size.</param>
/// <param name="byteName">The string used for the byte name. If null is passed, "B" will be used.</param>
/// <param name="numberFormat">The number format. If null is passed, "N2" will be used.</param>
/// <param name="formatProvider">The format provider. May be null to use current culture.</param>
/// <returns>The number converted.</returns>
public static string FormatFileSize(long size, string byteName, string numberFormat, IFormatProvider formatProvider)
{
if (size < 0)
throw new ArgumentException(null, "size");
if (byteName == null)
{
byteName = "B";
}
if (string.IsNullOrEmpty(numberFormat))
{
numberFormat = "N2";
}
const decimal K = 1024;
const decimal M = K * K;
const decimal G = M * K;
const decimal T = G * K;
const decimal P = T * K;
const decimal E = P * K;
decimal dsize = size;
string suffix = null;
if (dsize >= E)
{
dsize /= E;
suffix = "E";
}
else if (dsize >= P)
{
dsize /= P;
suffix = "P";
}
else if (dsize >= T)
{
dsize /= T;
suffix = "T";
}
else if (dsize >= G)
{
dsize /= G;
suffix = "G";
}
else if (dsize >= M)
{
dsize /= M;
suffix = "M";
}
else if (dsize >= K)
{
dsize /= K;
suffix = "k";
}
if (suffix != null)
{
suffix = " " + suffix;
}
return string.Format(formatProvider, "{0:" + numberFormat + "}" + suffix + byteName, dsize);
}
Here is an extension with more precision:
public static string FileSizeFormat(this long lSize)
{
double size = lSize;
int index = 0;
for(; size > 1024; index++)
size /= 1024;
return size.ToString("0.000 " + new[] { "B", "KB", "MB", "GB", "TB" }[index]);
}
0
A Domain Driven Approach can be found here: https://github.com/Corniel/Qowaiv/blob/master/src/Qowaiv/IO/StreamSize.cs
The 13 StreamSize struct is a representation of 12 a stream size, allows you both to format 11 automatic with the proper extension, but 10 also to specify that you want it in KB/MB 9 or whatever. This has a lot of advantages, not 8 only because you get the formatting out 7 of the box, it also helps you to make better 6 models, as it is obvious than, that the 5 property or the result of a method represents 4 a stream size. It also has an extension 3 on file size: GetStreamSize(this FileInfo 2 file).
Short notation
- new StreamSize(8900).ToString("s") => 8900b
- new StreamSize(238900).ToString("s") => 238.9kb
- new StreamSize(238900).ToString(" S") => 238.9 kB
- new StreamSize(238900).ToString("0000.00 S") => 0238.90 kB
Full notation
- new StreamSize(8900).ToString("0.0 f") => 8900.0 byte
- new StreamSize(238900).ToString("0 f") => 234 kilobyte
- new StreamSize(1238900).ToString("0.00 F") => 1.24 Megabyte
Custom
- new StreamSize(8900).ToString("0.0 kb") => 8.9 kb
- new StreamSize(238900).ToString("0.0 MB") => 0.2 MB
- new StreamSize(1238900).ToString("#,##0.00 Kilobyte") => 1,239.00 Kilobyte
- new StreamSize(1238900).ToString("#,##0") => 1,238,900
There is a NuGet-package, so you just 1 can use that one: https://www.nuget.org/packages/Qowaiv
I have taken Eduardo's answer and combined 2 it with a similar example from elsewhere 1 to provide additional options for the formatting.
public class FileSizeFormatProvider : IFormatProvider, ICustomFormatter
{
public object GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter))
{
return this;
}
return null;
}
private const string fileSizeFormat = "FS";
private const string kiloByteFormat = "KB";
private const string megaByteFormat = "MB";
private const string gigaByteFormat = "GB";
private const string byteFormat = "B";
private const Decimal oneKiloByte = 1024M;
private const Decimal oneMegaByte = oneKiloByte * 1024M;
private const Decimal oneGigaByte = oneMegaByte * 1024M;
public string Format(string format, object arg, IFormatProvider formatProvider)
{
//
// Ensure the format provided is supported
//
if (String.IsNullOrEmpty(format) || !(format.StartsWith(fileSizeFormat, StringComparison.OrdinalIgnoreCase) ||
format.StartsWith(kiloByteFormat, StringComparison.OrdinalIgnoreCase) ||
format.StartsWith(megaByteFormat, StringComparison.OrdinalIgnoreCase) ||
format.StartsWith(gigaByteFormat, StringComparison.OrdinalIgnoreCase)))
{
return DefaultFormat(format, arg, formatProvider);
}
//
// Ensure the argument type is supported
//
if (!(arg is long || arg is decimal || arg is int))
{
return DefaultFormat(format, arg, formatProvider);
}
//
// Try and convert the argument to decimal
//
Decimal size;
try
{
size = Convert.ToDecimal(arg);
}
catch (InvalidCastException)
{
return DefaultFormat(format, arg, formatProvider);
}
//
// Determine the suffix to use and convert the argument to the requested size
//
string suffix;
switch (format.Substring(0, 2).ToUpper())
{
case kiloByteFormat:
size = size / oneKiloByte;
suffix = kiloByteFormat;
break;
case megaByteFormat:
size = size / oneMegaByte;
suffix = megaByteFormat;
break;
case gigaByteFormat:
size = size / oneGigaByte;
suffix = gigaByteFormat;
break;
case fileSizeFormat:
if (size > oneGigaByte)
{
size /= oneGigaByte;
suffix = gigaByteFormat;
}
else if (size > oneMegaByte)
{
size /= oneMegaByte;
suffix = megaByteFormat;
}
else if (size > oneKiloByte)
{
size /= oneKiloByte;
suffix = kiloByteFormat;
}
else
{
suffix = byteFormat;
}
break;
default:
suffix = byteFormat;
break;
}
//
// Determine the precision to use
//
string precision = format.Substring(2);
if (String.IsNullOrEmpty(precision))
{
precision = "2";
}
return String.Format("{0:N" + precision + "}{1}", size, suffix);
}
private static string DefaultFormat(string format, object arg, IFormatProvider formatProvider)
{
IFormattable formattableArg = arg as IFormattable;
if (formattableArg != null)
{
return formattableArg.ToString(format, formatProvider);
}
return arg.ToString();
}
}
If you change:
if (String.IsNullOrEmpty(precision))
{
precision = "2";
}
into
if (String.IsNullOrEmpty(precision))
{
if (size < 10)
{
precision = "2";
}
else if (size < 100)
{
precision = "1";
}
else
{
precision = "0";
}
}
the results without 4 additional precision specifier (so just 3 0:fs instead of 0:fs3) will start to mimic 2 Win32's StrFormatByteSize() by adjusting 1 precision to size.
using C# 9.0 syntax can be written like 1 this:
public static string ToFormatSize(ulong size)
{
return size switch
{
ulong s when s < 1024 => $"{size} bytes",
ulong s when s < (1024 << 10) => $"{Math.Round(size / 1024D, 2)} KB",
ulong s when s < (1024 << 20) => $"{Math.Round(size * 1D / (1024 << 10), 2)} MB",
ulong s when s < (1024 << 30) => $"{Math.Round(size * 1D / (1024 << 20), 2)} GB",
ulong s when s < (1024 << 40) => $"{Math.Round(size * 1D / (1024 << 30), 2)} TB",
ulong s when s < (1024 << 50) => $"{Math.Round(size * 1D / (1024 << 40), 2)} PB",
ulong s when s < (1024 << 60) => $"{Math.Round(size * 1D / (1024 << 50), 2)} EB",
_ => $"{size} bytes"
};
}
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.