¿Cómo obtengo un tamaño de archivo legible para humanos en una abreviatura de bytes usando .NET?

¿Cómo obtengo un tamaño de archivo legible para humanos en una abreviatura de bytes usando .NET?

Ejemplo : tome la entrada 7,326,629 y muestre 6,98 MB

Esta no es la forma más eficiente de hacerlo, pero es más fácil de leer si no está familiarizado con las matemáticas de registro, y debe ser lo suficientemente rápido para la mayoría de los escenarios.

string[] sizes = { "B", "KB", "MB", "GB", "TB" }; double len = new FileInfo(filename).Length; int order = 0; while (len >= 1024 && order < sizes.Length - 1) { order++; len = len/1024; } // Adjust the format string to your preferences. For example "{0:0.#}{1}" would // show a single decimal place, and no space. string result = String.Format("{0:0.##} {1}", len, sizes[order]); 

usando Log para resolver el problema …

 static String BytesToString(long byteCount) { string[] suf = { "B", "KB", "MB", "GB", "TB", "PB", "EB" }; //Longs run out around EB if (byteCount == 0) return "0" + suf[0]; long bytes = Math.Abs(byteCount); int place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024))); double num = Math.Round(bytes / Math.Pow(1024, place), 1); return (Math.Sign(byteCount) * num).ToString() + suf[place]; } 

También en c #, pero debe ser un complemento para convertir. También redondeé a 1 lugar decimal para la legibilidad.

Básicamente Determine el número de lugares decimales en la Base 1024 y luego divida por 1024 ^ decimales.

Y algunas muestras de uso y salida:

 Console.WriteLine(BytesToString(9223372036854775807)); //Results in 8EB Console.WriteLine(BytesToString(0)); //Results in 0B Console.WriteLine(BytesToString(1024)); //Results in 1KB Console.WriteLine(BytesToString(2000000)); //Results in 1.9MB Console.WriteLine(BytesToString(-9023372036854775807)); //Results in -7.8EB 

Editar: Se señaló que me perdí un piso de matemáticas, así que lo incorporé. (Convert.ToInt32 usa redondeo, no trunca y es por eso que Floor es necesario.) Gracias por la captura.

Edit2: Hubo un par de comentarios sobre tamaños negativos y tamaños de 0 bytes, por lo que actualicé para manejar esos 2 casos.

Una versión probada y significativamente optimizada de la función solicitada se publica aquí:

Tamaño de archivo de lectura humana C # – Función optimizada

Código fuente:

 // Returns the human-readable file size for an arbitrary, 64-bit file size // The default format is "0.### XB", eg "4.2 KB" or "1.434 GB" public string GetBytesReadable(long i) { // Get absolute value long absolute_i = (i < 0 ? -i : i); // Determine the suffix and readable value string suffix; double readable; if (absolute_i >= 0x1000000000000000) // Exabyte { suffix = "EB"; readable = (i >> 50); } else if (absolute_i >= 0x4000000000000) // Petabyte { suffix = "PB"; readable = (i >> 40); } else if (absolute_i >= 0x10000000000) // Terabyte { suffix = "TB"; readable = (i >> 30); } else if (absolute_i >= 0x40000000) // Gigabyte { suffix = "GB"; readable = (i >> 20); } else if (absolute_i >= 0x100000) // Megabyte { suffix = "MB"; readable = (i >> 10); } else if (absolute_i >= 0x400) // Kilobyte { suffix = "KB"; readable = i; } else { return i.ToString("0 B"); // Byte } // Divide by 1024 to get fractional value readable = (readable / 1024); // Return formatted number with suffix return readable.ToString("0.### ") + suffix; } 
 [DllImport ( "Shlwapi.dll", CharSet = CharSet.Auto )] public static extern long StrFormatByteSize ( long fileSize , [MarshalAs ( UnmanagedType.LPTStr )] StringBuilder buffer , int bufferSize ); ///  /// Converts a numeric value into a string that represents the number expressed as a size value in bytes, kilobytes, megabytes, or gigabytes, depending on the size. ///  /// The numeric value to be converted. /// the converted string public static string StrFormatByteSize (long filesize) { StringBuilder sb = new StringBuilder( 11 ); StrFormatByteSize( filesize, sb, sb.Capacity ); return sb.ToString(); } 

De: http://www.pinvoke.net/default.aspx/shlwapi/StrFormatByteSize.html

Una forma más de desollarlo, sin ningún tipo de bucles y con soporte de tamaño negativo (tiene sentido para cosas como deltas de tamaño de archivo):

 public static class Format { static string[] sizeSuffixes = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" }; public static string ByteSize(long size) { Debug.Assert(sizeSuffixes.Length > 0); const string formatTemplate = "{0}{1:0.#} {2}"; if (size == 0) { return string.Format(formatTemplate, null, 0, sizeSuffixes[0]); } var absSize = Math.Abs((double)size); var fpPower = Math.Log(absSize, 1000); var intPower = (int)fpPower; var iUnit = intPower >= sizeSuffixes.Length ? sizeSuffixes.Length - 1 : intPower; var normSize = absSize / Math.Pow(1000, iUnit); return string.Format( formatTemplate, size < 0 ? "-" : null, normSize, sizeSuffixes[iUnit]); } } 

Y aquí está el conjunto de pruebas:

 [TestFixture] public class ByteSize { [TestCase(0, Result="0 B")] [TestCase(1, Result = "1 B")] [TestCase(1000, Result = "1 KB")] [TestCase(1500000, Result = "1.5 MB")] [TestCase(-1000, Result = "-1 KB")] [TestCase(int.MaxValue, Result = "2.1 GB")] [TestCase(int.MinValue, Result = "-2.1 GB")] [TestCase(long.MaxValue, Result = "9.2 EB")] [TestCase(long.MinValue, Result = "-9.2 EB")] public string Format_byte_size(long size) { return Format.ByteSize(size); } } 

Me gusta usar el siguiente método (admite hasta terabytes, que es suficiente para la mayoría de los casos, pero puede ampliarse fácilmente):

 private string GetSizeString(long length) { long B = 0, KB = 1024, MB = KB * 1024, GB = MB * 1024, TB = GB * 1024; double size = length; string suffix = nameof(B); if (length >= TB) { size = Math.Round((double)length / TB, 2); suffix = nameof(TB); } else if (length >= GB) { size = Math.Round((double)length / GB, 2); suffix = nameof(GB); } else if (length >= MB) { size = Math.Round((double)length / MB, 2); suffix = nameof(MB); } else if (length >= KB) { size = Math.Round((double)length / KB, 2); suffix = nameof(KB); } return $"{size} {suffix}"; } 

Tenga en cuenta que esto está escrito para C # 6.0 (2015), por lo que podría necesitar una pequeña edición para versiones anteriores.

 int size = new FileInfo( filePath ).Length / 1024; string humanKBSize = string.Format( "{0} KB", size ); string humanMBSize = string.Format( "{0} MB", size / 1024 ); string humanGBSize = string.Format( "{0} GB", size / 1024 / 1024 ); 

Consulte la biblioteca ByteSize . Es el System.TimeSpan para bytes!

Maneja la conversión y el formato para usted.

 var maxFileSize = ByteSize.FromKiloBytes(10); maxFileSize.Bytes; maxFileSize.MegaBytes; maxFileSize.GigaBytes; 

También realiza representación y análisis de cadenas.

 // ToString ByteSize.FromKiloBytes(1024).ToString(); // 1 MB ByteSize.FromGigabytes(.5).ToString(); // 512 MB ByteSize.FromGigabytes(1024).ToString(); // 1 TB // Parsing ByteSize.Parse("5b"); ByteSize.Parse("1.55B"); 
 string[] suffixes = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" }; int s = 0; long size = fileInfo.Length; while (size >= 1024) { s++; size /= 1024; } string humanReadable = String.Format("{0} {1}", size, suffixes[s]); 

Si intenta hacer coincidir el tamaño como se muestra en la vista de detalles del Explorador de Windows, este es el código que desea:

 [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] private static extern long StrFormatKBSize( long qdw, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder pszBuf, int cchBuf); public static string BytesToString(long byteCount) { var sb = new StringBuilder(32); StrFormatKBSize(byteCount, sb, sb.Capacity); return sb.ToString(); } 

Esto no solo coincidirá exactamente con Explorer, sino que también proporcionará las cadenas traducidas para usted y las diferencias de coincidencia en las versiones de Windows (por ejemplo, en Win10, K = 1000 frente a versiones anteriores, K = 1024).

Mezcla de todas las soluciones 🙂

  ///  /// Converts a numeric value into a string that represents the number expressed as a size value in bytes, /// kilobytes, megabytes, or gigabytes, depending on the size. ///  /// The numeric value to be converted. /// The converted string. public static string FormatByteSize(double fileSize) { FileSizeUnit unit = FileSizeUnit.B; while (fileSize >= 1024 && unit < FileSizeUnit.YB) { fileSize = fileSize / 1024; unit++; } return string.Format("{0:0.##} {1}", fileSize, unit); } ///  /// Converts a numeric value into a string that represents the number expressed as a size value in bytes, /// kilobytes, megabytes, or gigabytes, depending on the size. ///  ///  /// The converted string. public static string FormatByteSize(FileInfo fileInfo) { return FormatByteSize(fileInfo.Length); } } public enum FileSizeUnit : byte { B, KB, MB, GB, TB, PB, EB, ZB, YB } 

Hay un proyecto de código abierto que puede hacer eso y mucho más.

 7.Bits().ToString(); // 7 b 8.Bits().ToString(); // 1 B (.5).Kilobytes().Humanize(); // 512 B (1000).Kilobytes().ToString(); // 1000 KB (1024).Kilobytes().Humanize(); // 1 MB (.5).Gigabytes().Humanize(); // 512 MB (1024).Gigabytes().ToString(); // 1 TB 

http://humanizr.net/#bytesize

https://github.com/MehdiK/Humanizer

¿Supongo que está buscando “1.4 MB” en lugar de “1468006 bytes”?

No creo que haya una forma integrada de hacerlo en .NET. Tendrá que averiguar qué unidad es la adecuada y formatearla.

Editar: Aquí hay un código de muestra para hacer justamente eso:

http://www.codeproject.com/KB/cpp/formatsize.aspx

¿Qué tal una recursión?

 private static string ReturnSize(double size, string sizeLabel) { if (size > 1024) { if (sizeLabel.Length == 0) return ReturnSize(size / 1024, "KB"); else if (sizeLabel == "KB") return ReturnSize(size / 1024, "MB"); else if (sizeLabel == "MB") return ReturnSize(size / 1024, "GB"); else if (sizeLabel == "GB") return ReturnSize(size / 1024, "TB"); else return ReturnSize(size / 1024, "PB"); } else { if (sizeLabel.Length > 0) return string.Concat(size.ToString("0.00"), sizeLabel); else return string.Concat(size.ToString("0.00"), "Bytes"); } } 

Entonces lo llamas:

 return ReturnSize(size, string.Empty); 

 public static string ToBytesCount(this long bytes) { int unit = 1024; if (bytes < unit) return bytes + " o"; int exp = (int)(Math.Log(bytes) / Math.Log(unit)); return string.Format("{0:##.##} {1}o", bytes / Math.Pow(unit, exp), "KMGTPE"[exp - 1]); } 

Mis 2 centavos:

  • El prefijo para kilobyte es kB (K minúscula)
  • Como estas funciones son para fines de presentación, se debe proporcionar una cultura, por ejemplo: string.Format(CultureInfo.CurrentCulture, "{0:0.##} {1}", fileSize, unit);
  • Dependiendo del contexto, un kilobyte puede ser 1000 o 1024 bytes . Lo mismo vale para MB, GB, etc.

Un enfoque más, por lo que vale. Me gustó la solución optimizada de @humbads mencionada anteriormente, así que he copiado el principio, pero lo he implementado de forma un poco diferente.

Supongo que es discutible si debe ser un método de extensión (ya que no todos los largos son necesariamente tamaños de byte), pero me gustan, ¡y es un lugar en el que puedo encontrar el método la próxima vez que lo necesite!

En cuanto a las unidades, no creo haber dicho alguna vez ‘Kibibyte’ o ‘Mebibyte’ en mi vida, y si bien soy escéptico de tales normas impuestas en lugar de evolucionadas, supongo que evitará la confusión a largo plazo. .

 public static class LongExtensions { private static readonly long[] numberOfBytesInUnit; private static readonly Func[] bytesToUnitConverters; static LongExtensions() { numberOfBytesInUnit = new long[6] { 1L < < 10, // Bytes in a Kibibyte 1L << 20, // Bytes in a Mebibyte 1L << 30, // Bytes in a Gibibyte 1L << 40, // Bytes in a Tebibyte 1L << 50, // Bytes in a Pebibyte 1L << 60 // Bytes in a Exbibyte }; // Shift the long (integer) down to 1024 times its number of units, convert to a double (real number), // then divide to get the final number of units (units will be in the range 1 to 1023.999) Func FormatAsProportionOfUnit = (bytes, shift) => (((double)(bytes >> shift)) / 1024).ToString("0.###"); bytesToUnitConverters = new Func[7] { bytes => bytes.ToString() + " B", bytes => FormatAsProportionOfUnit(bytes, 0) + " KiB", bytes => FormatAsProportionOfUnit(bytes, 10) + " MiB", bytes => FormatAsProportionOfUnit(bytes, 20) + " GiB", bytes => FormatAsProportionOfUnit(bytes, 30) + " TiB", bytes => FormatAsProportionOfUnit(bytes, 40) + " PiB", bytes => FormatAsProportionOfUnit(bytes, 50) + " EiB", }; } public static string ToReadableByteSizeString(this long bytes) { if (bytes < 0) return "-" + Math.Abs(bytes).ToReadableByteSizeString(); int counter = 0; while (counter < numberOfBytesInUnit.Length) { if (bytes < numberOfBytesInUnit[counter]) return bytesToUnitConverters[counter](bytes); counter++; } return bytesToUnitConverters[counter](bytes); } } 

Como la solución de @ NET3. Use shift en lugar de división para probar el rango de bytes , porque la división requiere más costo de CPU.

 private static readonly string[] UNITS = new string[] { "B", "KB", "MB", "GB", "TB", "PB", "EB" }; public static string FormatSize(ulong bytes) { int c = 0; for (c = 0; c < UNITS.Length; c++) { ulong m = (ulong)1 << ((c + 1) * 10); if (bytes < m) break; } double n = bytes / (double)((ulong)1 << (c * 10)); return string.Format("{0:0.##} {1}", n, UNITS[c]); }