Como faço para obter uma abreviação de tamanho de arquivo em bytes com legibilidade humana usando o .NET?

Como faço para obter uma abreviação de tamanho de arquivo em bytes com legibilidade humana usando o .NET?

Exemplo : pegue a input 7.326.629 e exiba 6,98 MB

Essa não é a maneira mais eficiente de fazer isso, mas é mais fácil de ler se você não estiver familiarizado com os cálculos de log e deve ser rápido o suficiente para a maioria dos cenários.

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

Também em c #, mas deve ser um piscar de olhos para converter. Também arredondado para 1 casa decimal para legibilidade.

Basicamente Determine o número de casas decimais na Base 1024 e divida por 1024 ^ casas decimais.

E algumas amostras de uso e saída:

 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 

Edit: Foi apontado que eu perdi um math.floor, então eu o incorporei. (Convert.ToInt32 usa arredondamento, não truncando e é por isso que Floor é necessário.) Obrigado pela captura.

Edit2: Houve alguns comentários sobre tamanhos negativos e tamanhos de 0 bytes, então atualizei para lidar com esses dois casos.

Uma versão testada e significativamente otimizada da function solicitada é postada aqui:

Tamanho do arquivo legível em humanos C # – Função otimizada

Código fonte:

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

Mais uma maneira de eliminá-lo, sem nenhum tipo de loop e com suporte a tamanho negativo (faz sentido para coisas como deltas de tamanho de arquivo):

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

E aqui está a suíte de testes:

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

Eu gosto de usar o seguinte método (ele suporta até terabytes, o que é suficiente para a maioria dos casos, mas pode ser facilmente estendido):

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

Por favor, tenha em mente que isto está escrito para o C # 6.0 (2015), então pode precisar de um pouco de edição para versões 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 ); 

Faça check- out da biblioteca ByteSize . É o System.TimeSpan para bytes!

Ele lida com a conversão e formatação para você.

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

Ele também faz representação e análise de string.

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

Se você estiver tentando corresponder ao tamanho conforme mostrado na visualização detalhada do Windows Explorer, esse é o código que você deseja:

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

Isso não apenas corresponderá ao Explorer exatamente, mas também fornecerá as sequências traduzidas para você e corresponderá às diferenças nas versões do Windows (por exemplo, no Win10, K = 1000 versus versões anteriores K = 1024).

Mistura de todas as soluções 🙂

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

Existe um projeto de código aberto que pode fazer isso e muito mais.

 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

Eu suponho que você está procurando por “1,4 MB” em vez de “1468006 bytes”?

Eu não acho que há uma maneira interna de fazer isso no .net. Você precisará descobrir qual unidade é apropriada e formatá-la.

Edit: Aqui está um código de exemplo para fazer exatamente isso:

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

Que tal alguma recursion:

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

Então você chama isso:

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

Meus 2 centavos:

  • O prefixo para kilobyte é kB (K minúsculo)
  • Como essas funções são para fins de apresentação, deve-se fornecer uma cultura, por exemplo: string.Format(CultureInfo.CurrentCulture, "{0:0.##} {1}", fileSize, unit);
  • Dependendo do contexto, um kilobyte pode ser de 1000 ou 1024 bytes . O mesmo vale para MB, GB, etc.

Mais uma abordagem, pelo que vale a pena. Gostei da solução otimizada do @humbads citada acima, então copiei o princípio, mas o implementei de forma um pouco diferente.

Eu suponho que seja discutível se deve ser um método de extensão (já que nem todos os longs são necessariamente tamanhos de byte), mas eu gosto deles, e é em algum lugar que eu posso encontrar o método quando eu precisar dele!

Em relação às unidades, eu acho que nunca disse ‘Kibibyte’ ou ‘Mebibyte’ em minha vida, e embora eu seja cético em relação a esses padrões reforçados em vez de evoluídos, eu suponho que isso evitará confusão a longo prazo. .

 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 a solução do @ NET3. Use shift em vez de division para testar o intervalo de bytes , porque a divisão leva mais custo da 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]); }