Proveedor de formato de tamaño de archivo

¿Hay alguna manera fácil de crear una clase que use IFormatProvider que escriba un tamaño de archivo fácil de usar?

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... } 

Debería resultar en cadenas formateadas como ” 2,5 MB “, ” 3,9 GB “, ” 670 bytes “, etc.

Yo uso este, lo obtengo de la 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(); } } 

un ejemplo de uso sería:

 Console.WriteLine(String.Format(new FileSizeFormatProvider(), "File size: {0:fs}", 100)); Console.WriteLine(String.Format(new FileSizeFormatProvider(), "File size: {0:fs}", 10000)); 

Créditos para http://flimflan.com/blog/FileSizeFormatProvider.aspx

Hay un problema con ToString (), está esperando un tipo NumberFormatInfo que implementa IFormatProvider pero la clase NumberFormatInfo está sellada 🙁

Si usa C # 3.0, puede usar un método de extensión para obtener el resultado que desea:

 public static class ExtensionMethods { public static string ToFileSize(this long l) { return String.Format(new FileSizeFormatProvider(), "{0:fs}", l); } } 

Puedes usarlo así

 long l = 100000000; Console.WriteLine(l.ToFileSize()); 

Espero que esto ayude.

De acuerdo, no voy a concluir como proveedor de formatos, pero en lugar de reinventar la rueda, hay una llamada api de Win32 para formatear una cadena de tamaño basada en los bytes suministrados que he utilizado muchas veces en varias aplicaciones.

 [DllImport("Shlwapi.dll", CharSet = CharSet.Auto)] public static extern long StrFormatByteSize( long fileSize, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder buffer, int bufferSize ); 

Así que imagino que debería ser capaz de armar un proveedor que lo use como el código de conversión principal.

Aquí hay un enlace a la especificación de MSDN para StrFormatByteSize.

Ahora me doy cuenta de que realmente estabas pidiendo algo que funcionara con String.Format () – Creo que debería haber leído la pregunta dos veces antes de publicar 😉

No me gusta la solución en la que tiene que pasar explícitamente en un proveedor de formatos todo el tiempo. Por lo que pude deducir de este artículo , la mejor manera de abordar esto es implementar un tipo FileSize que implemente la interfaz IFormattable.

Seguí adelante e implementé una estructura que admite esta interfaz, y que se puede convertir desde un número entero. En mis propias API relacionadas con archivos, haré que mis propiedades .FileSize devuelvan una instancia de FileSize.

Aquí está el código:

 using System.Globalization; public struct FileSize : IFormattable { private ulong _value; private const int DEFAULT_PRECISION = 2; private static IList Units; static FileSize() { Units = new List(){ "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); } ///  /// Formats the FileSize using the given number of decimals. ///  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]; } } 

Y una simple prueba de unidad que demuestra cómo funciona esto:

  [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"); } 

Como puede ver, el tipo FileSize ahora se puede formatear correctamente, y también es posible especificar el número de decimales, así como también aplicar el formato numérico regular si es necesario.

Supongo que podría llevar esto mucho más allá, por ejemplo, permitiendo la selección explícita de formato, por ejemplo, “{0: KB}” para forzar el formateo en kilobytes. Pero lo dejaré en esto.

También dejo mi publicación inicial a continuación, ya que los dos prefieren no usar la API de formato …


100 maneras de despellejar a un gato, pero este es mi enfoque: agregar un método de extensión al tipo int:

 public static class IntToBytesExtension { private const int PRECISION = 2; private static IList Units; static IntToBytesExtension() { Units = new List(){ "B", "KB", "MB", "GB", "TB" }; } ///  /// Formats the value as a filesize in bytes (KB, MB, etc.) ///  /// This value. /// Filesize and quantifier formatted as a string. 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]; } } 

Con esta extensión en su ensamblaje, para formatear un tamaño de archivo, simplemente use una instrucción como (1234567) .ToBytes ()

La siguiente prueba de MbUnit aclara con precisión cómo se ve la salida:

  [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()); } 

Y puede cambiar fácilmente las unidades y la precisión para adaptarse a sus necesidades 🙂

esta es la implementación más simple que conozco para formatear los tamaños de archivo:

 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]); } } 

Mientras que Tamaño es el tamaño del archivo sin formato en bytes.

Saludos Christian

http://www.wpftutorial.net

Mi código … gracias por 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(); } 

ya que el cambio es una operación muy barata

 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"; } } 

Tomé la respuesta de Eduardo y la combiné con un ejemplo similar de otra parte para proporcionar opciones adicionales para el formateo.

 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(); } } 

Necesitaba una versión que se pueda localizar para diferentes culturas (separador decimal, traducción “byte”) y soporte para todos los posibles prefijos binarios (hasta Exa). Aquí hay un ejemplo que demuestra cómo usarlo:

 // 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))); 

Y aquí está el código:

  ///  /// 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 ///  /// The size. ///  /// The number converted. ///  public static string FormatFileSize(long size) { return FormatFileSize(size, null, null, null); } ///  /// 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 ///  /// The size. /// The string used for the byte name. If null is passed, "B" will be used. /// The number format. If null is passed, "N2" will be used. /// The format provider. May be null to use current culture. /// The number converted. 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); } 

Aquí hay una extensión con más precisión:

  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]); } 

Un enfoque impulsado por el dominio se puede encontrar aquí: https://github.com/Corniel/Qowaiv/blob/master/src/Qowaiv/IO/StreamSize.cs

La estructura StreamSize es una representación del tamaño de una secuencia, le permite a ambos formatear automáticamente con la extensión adecuada, pero también especificar que la quiere en KB / MB o lo que sea. Esto tiene muchas ventajas, no solo porque obtienes el formato de la caja, sino que también te ayuda a crear mejores modelos, ya que es obvio que la propiedad o el resultado de un método representa un tamaño de flujo. También tiene una extensión en el tamaño del archivo: GetStreamSize (este archivo FileInfo).

Notación corta

  • nuevo StreamSize (8900) .ToString (“s”) => 8900b
  • nuevo StreamSize (238900) .ToString (“s”) => 238.9kb
  • nuevo StreamSize (238900) .ToString (“S”) => 238.9 kB
  • nuevo StreamSize (238900) .ToString (“0000.00 S”) => 0238.90 kB

Notación completa

  • nuevo StreamSize (8900) .ToString (“0.0 f”) => 8900.0 byte
  • nuevo StreamSize (238900) .ToString (“0 f”) => 234 kilobytes
  • nuevo StreamSize (1238900) .ToString (“0.00 F”) => 1.24 Megabyte

Personalizado

  • nuevo StreamSize (8900) .ToString (“0.0 kb”) => 8.9 kb
  • nuevo StreamSize (238900) .ToString (“0.0 MB”) => 0.2 MB
  • nuevo StreamSize (1238900) .ToString (“#, ## 0.00 Kilobyte”) => 1,239.00 Kilobyte
  • nuevo StreamSize (1238900) .ToString (“#, ## 0”) => 1,238,900

Hay un paquete NuGet, así que solo puede usar ese: https://www.nuget.org/packages/Qowaiv

Si cambias:

  if (String.IsNullOrEmpty(precision)) { precision = "2"; } 

dentro

  if (String.IsNullOrEmpty(precision)) { if (size < 10) { precision = "2"; } else if (size < 100) { precision = "1"; } else { precision = "0"; } } 

los resultados sin especificador de precisión adicional (tan solo 0: fs en lugar de 0: fs3) comenzarán a imitar el StrFormatByteSize () de Win32 ajustando la precisión al tamaño.