文件大小的格式提供者
有没有简单的方法来创build一个使用IFormatProvider的类,写出一个用户友好的文件大小?
public static string GetFileSizeString(string filePath) { FileInfo info = new FileInfo(@"c:\windows\notepad.exe"); long size = info.Length; string sizeString = size.ToString(FileSizeFormatProvider); // This is where the class does its magic... }
它应该导致string格式化如“ 2,5 MB ”,“ 3,9 GB ”,“ 670字节 ”等等。
我使用这个,我从网上获得
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(); } }
一个使用的例子是:
Console.WriteLine(String.Format(new FileSizeFormatProvider(), "File size: {0:fs}", 100)); Console.WriteLine(String.Format(new FileSizeFormatProvider(), "File size: {0:fs}", 10000));
积分http://flimflan.com/blog/FileSizeFormatProvider.aspx
ToString()存在一个问题,期望NumberFormatInfotypes实现IFormatProvider,但NumberFormatInfo类是密封的:(
如果您使用的是C#3.0,则可以使用扩展方法来获取所需的结果:
public static class ExtensionMethods { public static string ToFileSize(this long l) { return String.Format(new FileSizeFormatProvider(), "{0:fs}", l); } }
你可以像这样使用它。
long l = 100000000; Console.WriteLine(l.ToFileSize());
希望这可以帮助。
好吧,我不打算把它作为一个格式提供程序包装起来,而不是重新发明轮子有一个Win32 api调用格式化大小string基于提供的字节,我已经在各种应用程序中使用了很多次。
[DllImport("Shlwapi.dll", CharSet = CharSet.Auto)] public static extern long StrFormatByteSize( long fileSize, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder buffer, int bufferSize );
所以我想你应该能够把一个提供商作为核心转换代码。
这是一个指向 StrFormatByteSize的MSDN规范的链接。
我现在意识到,你实际上是要求一些可以与String.Format()一起工作的东西 – 我想我应该在发布之前两次阅读这个问题;-)
我不喜欢每次必须显式传递一个格式提供者的解决scheme,从我从本文中可以获得的最好的方法是实现一个FileSizetypes,实现IFormattable接口。
我继续前进,并实现了一个支持此接口的结构,并且可以从一个整型转换。 在我自己的文件相关的API中,我将有我的.FileSize属性返回一个FileSize实例。
代码如下:
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]; } }
以及一个简单的unit testing,演示如何工作:
[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"); }
正如你所看到的,FileSizetypes现在可以被正确格式化,并且也可以指定小数的数量,以及如果需要的话应用常规的数字格式。
我想你可以进一步,例如允许明确的格式select,例如“{0:KB}”强制以千字节为单位的格式。 但是我要把它留在这里。
我也离开我的初始职位下面这两个不喜欢使用格式API …
100种皮肤猫的方法,但这是我的方法 – 添加一个扩展方法的inttypes:
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]; } }
有了这个扩展在你的程序集,格式化文件大小,只需使用像(1234567).ToBytes()
下面的MbUnittesting可以清楚地说明输出结果如何:
[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()); }
你可以很容易地改变单位和精度,以适合你的需要:-)
这是我知道格式化文件大小的最简单的实现:
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]); } }
而Size是以字节为单位的无格式文件大小。
问候基督徒
我的代码…感谢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(); }
因为转移是一个非常便宜的操作
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"; } }
我已经采取了Eduardo的答案,并将其与其他地方的类似示例结合起来,为格式提供更多选项。
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(); } }
我需要一个可以针对不同文化进行本地化的版本(十进制分隔符,“字节”转换),并支持所有可能的二进制前缀 (Exa)。 这里是一个演示如何使用它的例子:
// 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)));
这里是代码:
/// <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); }
域驱动的方法可以在这里find: https : //github.com/Corniel/Qowaiv/blob/master/src/Qowaiv/IO/StreamSize.cs
StreamSize结构是stream大小的表示forms,允许您使用正确的扩展名自动格式化,还可以指定以KB / MB或其他forms指定的值。 这样做有很多好处,不仅仅是因为你获得了格式化,它还可以帮助你创build更好的模型,因为显而易见,方法的属性或结果代表了stream的大小。 它也有一个文件大小的扩展:GetStreamSize(这个FileInfo文件)。
简短的符号
- 新的StreamSize(8900).ToString(“s”)=> 8900b
- 新的StreamSize(238900).ToString(“s”)=> 238.9kb
- 新的StreamSize(238900).ToString(“S”)=> 238.9 kB
- 新的StreamSize(238900).ToString(“0000.00 S”)=> 0238.90 kB
完整的符号
- 新的StreamSize(8900).ToString(“0.0 f”)=> 8900.0字节
- 新的StreamSize(238900).ToString(“0 f”)=> 234千字节
- 新的StreamSize(1238900).ToString(“0.00 F”)=> 1.24兆字节
习惯
- 新的StreamSize(8900).ToString(“0.0 kb”)=> 8.9 kb
- 新的StreamSize(238900).ToString(“0.0 MB”)=> 0.2 MB
- 新的StreamSize(1238900).ToString(“#,## 0.00 Kilobyte”)=> 1,239.00 Kilobyte
- 新的StreamSize(1238900).ToString(“#,## 0”)=> 1,238,900
有一个NuGet包,所以你可以使用那个: https : //www.nuget.org/packages/Qowaiv
如果你改变:
if (String.IsNullOrEmpty(precision)) { precision = "2"; }
成
if (String.IsNullOrEmpty(precision)) { if (size < 10) { precision = "2"; } else if (size < 100) { precision = "1"; } else { precision = "0"; } }
没有额外的精度说明符的结果(所以只是0:fs而不是0:fs3)将开始模仿Win32的StrFormatByteSize()通过调整精度的大小。
这是一个更精确的扩展:
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]); }