Essa não é a maneira mais eficiente de fazer isso, mas é mais fácil de ler se você não estiver familiarizado com a matemática de log e deve ser rápido o suficiente para a maioria dos cenários.
string[] sizes ={"B","KB","MB","GB","TB"};double len =newFileInfo(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]);
Eu acredito que você poderia usar Math.Log para determinar a ordem em vez de usar um loop while.
precisa
12
Além disso, o KB tem 1000 bytes. 1024 bytes é KiB .
Constantin
12
@ Constant bem, isso depende do sistema operacional? Windows ainda conta 1024 bytes como 1 KB e 1 MB = 1024 KB, Pessoalmente eu quero jogar o KiB para fora da janela e apenas contar cada coisa usando 1024 ...?
Peter
4
@Petoj, não depende do sistema operacional, a definição é independente do sistema operacional. Da Wikipedia:The unit was established by the International Electrotechnical Commission (IEC) in 1998 and has been accepted for use by all major standards organizations
ANeves
3
Eu prefiro esse código, pois ele parece correr mais rápido, mas eu o modifiquei um pouco para permitir diferentes números de casas decimais. Números menores mostram melhor 2 casas decimais, por exemplo, 1,38 MB, enquanto que números maiores exigem menos casas decimais, por exemplo, 246k ou 23,5KB:
Myke Black
320
usando o Log para resolver o problema ....
staticStringBytesToString(long byteCount){string[] suf ={"B","KB","MB","GB","TB","PB","EB"};//Longs run out around EBif(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 uma casa decimal para facilitar a leitura.
Basicamente, determine o número de casas decimais na Base 1024 e, em seguida, divida por 1024 ^ casas decimais.
E algumas amostras de uso e saída:
Console.WriteLine(BytesToString(9223372036854775807));//Results in 8EBConsole.WriteLine(BytesToString(0));//Results in 0BConsole.WriteLine(BytesToString(1024));//Results in 1KBConsole.WriteLine(BytesToString(2000000));//Results in 1.9MBConsole.WriteLine(BytesToString(-9023372036854775807));//Results in -7.8EB
Edit: Foi indicado que eu perdi um math.floor, então eu o incorporei. (Convert.ToInt32 usa arredondamento, não truncamento, e é por isso que Floor é necessário.) Obrigado pela captura.
Edit2: Houve alguns comentários sobre tamanhos negativos e tamanhos de 0 byte, então atualizei para lidar com esses 2 casos.
Quero advertir que, embora essa resposta seja realmente um pequeno pedaço de código, ela não é a mais otimizada. Gostaria que você desse uma olhada no método postado por @humbads. Eu executei microtestes enviando 10.000.000 de tamanhos de arquivos gerados aleatoriamente através dos dois métodos, e isso mostra que o método dele é ~ 30% mais rápido. No entanto, eu fiz alguma limpeza adicional no método dele (atribuições desnecessárias e seleção de elenco). Além disso, eu executei um teste com um tamanho negativo (quando você está comparando arquivos), enquanto o método das humbads processa perfeitamente este método desse Log, lançando uma exceção!
precisa saber é o seguinte
1
Sim, você deve adicionar Math.Abs para tamanhos negativos. Além disso, o código não lidar com o caso, se o tamanho é exatamente 0.
dasheddot
Math.Abs, Math.Floor, Math.Log, Convertendo para número inteiro, Math.Round, Math.Pow, Math.Sign, Adicionando, Multiplicando, Dividindo? Não foi essa quantidade de matemática que causou um grande aumento no processador. Este é provavelmente mais lento do que o código @humbads
Jayson Ragasa
Falha em double.MaxValue(place = 102)
BrunoLM
Funciona bem! Para imitar a maneira como o Windows funciona (pelo menos no meu Windows 7 Ultimate), substitua o Math.Round por Math.Ceiling. Obrigado novamente. Eu gosto desta solução.
H_He
101
Uma versão testada e significativamente otimizada da função solicitada é postada aqui:
+1! Mais simples e direto! Faz com que o processador faça as contas com facilidade e rapidez!
Jayson Ragasa
Para sua informação, você não usa o valor em double readable = (i < 0 ? -i : i);nenhum lugar, então remova-o. mais uma coisa, o elenco é redaundat
Royi Namir
Eu removi o elenco, adicionei comentários e corrigi um problema com o sinal negativo.
humbads
Ótima resposta. Obrigado, por que não usar Math.Abs?
kspearrin
1
(i <0? -i: i) é aproximadamente 15% mais rápido que Math.Abs. Para um milhão de chamadas, o Math.Abs é 0,5 milissegundos mais lento na minha máquina - 3,2 ms vs 3,7 ms.
Humbads
72
[DllImport("Shlwapi.dll",CharSet=CharSet.Auto)]publicstaticexternlongStrFormatByteSize(long fileSize
,[MarshalAs(UnmanagedType.LPTStr)]StringBuilder buffer
,int bufferSize );/// <summary>/// 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./// </summary>/// <param name="filelength">The numeric value to be converted.</param>/// <returns>the converted string</returns>publicstaticstringStrFormatByteSize(long filesize){StringBuilder sb =newStringBuilder(11);StrFormatByteSize( filesize, sb, sb.Capacity);return sb.ToString();}
Eu posso ser um noob, mas usar canhões gigantescos como pinvoke para matar aquele pato é um grande uso indevido.
24411 Bart
27
É isso que o explorer usa? Nesse caso, é magnificamente útil para permitir que as pessoas correspondam ao tamanho do arquivo que você mostra a elas com o que o Explorer mostra.
Andrew Backer,
8
E aquele que não reinventar a roda
Matthew Bloqueio
11 caracteres não são um limite constante e um pouco baixo para isso? Quero dizer, outros idiomas podem usar mais caracteres para o acrônimo de tamanho de byte ou outros estilos de formatação.
Ray
1
@Bart leva um tempo para os noobs aprenderem a sabedoria nisto: "Devemos esquecer pequenas eficiências, digamos cerca de 97% das vezes: a otimização prematura é a raiz de todo mal" ubiquity.acm.org/article.cfm? id = 1513451
Matthew Lock
22
Mais uma maneira de usá-lo, sem nenhum tipo de loop e com suporte a tamanho negativo (faz sentido para coisas como deltas de tamanho de arquivo):
Boa resposta. Deve haver um problema quando o tamanho do arquivo é muito pequeno; nesse caso / 1024 retorna 0. Você pode usar um tipo e chamada fracionários Math.Ceilingou algo assim.
Nawfal
10
Aqui está uma resposta concisa que determina a unidade automaticamente.
Você deve verificar: while (tamanho> = 1024 && s <sufixos.Comprimento).
TcKs
não ... um número inteiro assinado de 64 bits não pode ir além do ZB ... que representa os números 2 ^ 70.
11138 Bobwienholt
7
Então, por que colocar em YB?
Configurator
Eu mesmo gosto dessa resposta, mas todos aqui colocam soluções realmente ineficientes, você deve usar o deslocamento "size = size >> 10" muito mais rápido que a divisão ... e acho que é bom ter o do especificador grego extra de lá, porque no futuro próximo, uma função DLR posiable wouldnt precisa do "tamanho de tempo .." você poderia estar em uma CPU de 128 bits vector ou algo que pode segurar ZB e maior;)
RandomNickName42
4
O deslocamento de bits foi mais eficiente do que a divisão nos dias de codificação C no metal. Você fez um teste de desempenho no .NET para ver se o deslocamento de bits é realmente mais eficiente? Há pouco tempo, observei o estado do xor-swap e descobri que era realmente mais lento no .NET vs usando uma variável temp.
Pete
7
Se você estiver tentando corresponder ao tamanho, conforme mostrado na exibição de detalhes do Windows Explorer, este é o código que você deseja:
Isso não apenas corresponderá exatamente ao Explorer, mas também fornecerá as seqüências traduzidas para você e as diferenças nas versões do Windows (por exemplo, no Win10, K = 1000 vs. versões anteriores K = 1024).
Esse código não é compilado, você precisa especificar a dll da qual a função veio. Portanto, o protótipo de toda a função se parece com: [DllImport ("shlwapi.dll", CharSet = CharSet.Auto, SetLastError = true)] público estático externo long StrFormatKBSize (long qdw, [MarshalAs (UnmanagedType.LPTStr)] StringBuilder pszBuf, int cchBuf ); Deixe-me ser o primeiro a favorecer esta solução. Por que reinventar a roda se a roda já foi inventada? Essa é uma abordagem típica de todos os programadores de C #, mas infelizmente o C # não atinge todos os destinos atingidos pelo C ++.
TarmoPikaro
E mais uma correção de erro: Int64.MaxValue atinge 9.223.372.036.854.775.807, o que requer alocar o tamanho do buffer de 25+ - eu o arredondei para 32 apenas por precaução (não 11, como no código de demonstração acima).
TarmoPikaro
Obrigado @TarmoPikaro. Quando copiei do meu código de trabalho, perdi o DllImport. Também aumentou o tamanho do buffer de acordo com sua recomendação. Boa pegada!
Metalogic 05/04
abordagem impressionante
tbhaxor
Isso mostra apenas a unidade KB. A idéia é mostrar a maior unidade, dependendo do valor.
jstuardo
5
Mistura de todas as soluções :-)
/// <summary>/// 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./// </summary>/// <param name="fileSize">The numeric value to be converted.</param>/// <returns>The converted string.</returns>publicstaticstringFormatByteSize(double fileSize){FileSizeUnit unit =FileSizeUnit.B;while(fileSize >=1024&& unit <FileSizeUnit.YB){
fileSize = fileSize /1024;
unit++;}returnstring.Format("{0:0.##} {1}", fileSize, unit);}/// <summary>/// 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./// </summary>/// <param name="fileInfo"></param>/// <returns>The converted string.</returns>publicstaticstringFormatByteSize(FileInfo fileInfo){returnFormatByteSize(fileInfo.Length);}}publicenumFileSizeUnit:byte{
B,
KB,
MB,
GB,
TB,
PB,
EB,
ZB,
YB
}
Como a solução da @ NET3. Use shift em vez de divisão para testar o intervalo bytes, porque a divisão leva mais custo da CPU.
privatestaticreadonlystring[] UNITS =newstring[]{"B","KB","MB","GB","TB","PB","EB"};publicstaticstringFormatSize(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));returnstring.Format("{0:0.##} {1}", n, UNITS[c]);}
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 ter 1000 ou 1024 bytes . O mesmo vale para MB, GB, etc.
Um kilobyte significa 1000 bytes ( wolframalpha.com/input/?i=kilobyte ), não depende do contexto. É historicamente dependia de contexto, como wikipedia diz, e era de jure mudou em 1998 e de facto a mudança começou por volta de 2005, quando terabyte discos rígidos trouxe a atenção do público. O termo para 1024 bytes é kibibyte. O código que os alterna com base na cultura está produzindo informações incorretas.
Superbest
1
Mais uma abordagem, pelo que vale a pena. Gostei da solução otimizada do @humbads mencionada acima, por isso copiei o princípio, mas o implementei de maneira um pouco diferente.
Suponho que seja discutível se deve ser um método de extensão (já que nem todos os comprimentos são necessariamente tamanhos de bytes), mas eu gosto deles, e é em algum lugar que posso encontrar o método na próxima vez que precisar!
Em relação às unidades, acho que nunca disse 'Kibibyte' ou 'Mebibyte' em minha vida, e embora eu seja cético em relação a esses padrões impostos em vez de desenvolvidos, acho que evitará confusão a longo prazo. .
publicstaticclassLongExtensions{privatestaticreadonlylong[] numberOfBytesInUnit;privatestaticreadonlyFunc<long,string>[] bytesToUnitConverters;staticLongExtensions(){
numberOfBytesInUnit =newlong[6]{1L<<10,// Bytes in a Kibibyte1L<<20,// Bytes in a Mebibyte1L<<30,// Bytes in a Gibibyte1L<<40,// Bytes in a Tebibyte1L<<50,// Bytes in a Pebibyte1L<<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<long,int,string>FormatAsProportionOfUnit=(bytes, shift)=>(((double)(bytes >> shift))/1024).ToString("0.###");
bytesToUnitConverters =newFunc<long,string>[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",};}publicstaticstringToReadableByteSizeString(thislong 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);}}
Eu uso o método de extensão Long abaixo para converter em uma sequência de tamanho legível por humanos. Este método é a implementação em C # da solução Java desta mesma pergunta postada no Stack Overflow, aqui .
/// <summary>/// Convert a byte count into a human readable size string./// </summary>/// <param name="bytes">The byte count.</param>/// <param name="si">Whether or not to use SI units.</param>/// <returns>A human readable size string.</returns>publicstaticstringToHumanReadableByteCount(thislong bytes
,bool si
){var unit = si
?1000:1024;if(bytes < unit){return $"{bytes} B";}var exp =(int)(Math.Log(bytes)/Math.Log(unit));return $"{bytes / Math.Pow(unit, exp):F2} "+
$"{(si ? "kMGTPE" : "KMGTPE")[exp - 1] + (si ? string.Empty : "i")}B";}
Respostas:
Essa não é a maneira mais eficiente de fazer isso, mas é mais fácil de ler se você não estiver familiarizado com a matemática de log e deve ser rápido o suficiente para a maioria dos cenários.
fonte
The unit was established by the International Electrotechnical Commission (IEC) in 1998 and has been accepted for use by all major standards organizations
usando o Log para resolver o problema ....
Também em c #, mas deve ser um piscar de olhos para converter. Também arredondado para uma casa decimal para facilitar a leitura.
Basicamente, determine o número de casas decimais na Base 1024 e, em seguida, divida por 1024 ^ casas decimais.
E algumas amostras de uso e saída:
Edit: Foi indicado que eu perdi um math.floor, então eu o incorporei. (Convert.ToInt32 usa arredondamento, não truncamento, e é por isso que Floor é necessário.) Obrigado pela captura.
Edit2: Houve alguns comentários sobre tamanhos negativos e tamanhos de 0 byte, então atualizei para lidar com esses 2 casos.
fonte
double.MaxValue
(place = 102)Uma versão testada e significativamente otimizada da função solicitada é postada aqui:
Tamanho do arquivo legível por humanos em C # - Função otimizada
Código fonte:
fonte
double readable = (i < 0 ? -i : i);
nenhum lugar, então remova-o. mais uma coisa, o elenco é redaundatMath.Abs
?De: http://www.pinvoke.net/default.aspx/shlwapi/StrFormatByteSize.html
fonte
Mais uma maneira de usá-lo, sem nenhum tipo de loop e com suporte a tamanho negativo (faz sentido para coisas como deltas de tamanho de arquivo):
E aqui está a suíte de testes:
fonte
Faça o checkout da biblioteca ByteSize . É
System.TimeSpan
para bytes!Ele lida com a conversão e formatação para você.
Ele também faz representação e análise de cadeias.
fonte
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):
Lembre-se de que isso foi escrito para o C # 6.0 (2015), portanto, pode ser necessário um pouco de edição para versões anteriores.
fonte
fonte
Math.Ceiling
ou algo assim.Aqui está uma resposta concisa que determina a unidade automaticamente.
"b" é para bit, "B" é para Byte e "KMGTPEZY" são respectivamente para quilo, mega, giga, tera, peta, exa, zetta e yotta
Pode-se expandi-lo para levar em consideração a ISO / IEC80000 :
fonte
o
KMGTPE posterior:byte
é francês ( éoctet
em francês). Para qualquer outro idioma, substitua oo
porb
fonte
Se você estiver tentando corresponder ao tamanho, conforme mostrado na exibição de detalhes do Windows Explorer, este é o código que você deseja:
Isso não apenas corresponderá exatamente ao Explorer, mas também fornecerá as seqüências traduzidas para você e as diferenças nas versões do Windows (por exemplo, no Win10, K = 1000 vs. versões anteriores K = 1024).
fonte
Mistura de todas as soluções :-)
fonte
Há um projeto de código aberto que pode fazer isso e muito mais.
http://humanizr.net/#bytesize
https://github.com/MehdiK/Humanizer
fonte
Como a solução da @ NET3. Use shift em vez de divisão para testar o intervalo
bytes
, porque a divisão leva mais custo da CPU.fonte
Suponho que você esteja procurando "1,4 MB" em vez de "1468006 bytes"?
Eu não acho que exista uma maneira interna de fazer isso no .NET. Você precisará apenas 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
fonte
Que tal alguma recursão:
Então você chama:
fonte
Meus 2 centavos:
string.Format(CultureInfo.CurrentCulture, "{0:0.##} {1}", fileSize, unit);
fonte
Mais uma abordagem, pelo que vale a pena. Gostei da solução otimizada do @humbads mencionada acima, por isso copiei o princípio, mas o implementei de maneira um pouco diferente.
Suponho que seja discutível se deve ser um método de extensão (já que nem todos os comprimentos são necessariamente tamanhos de bytes), mas eu gosto deles, e é em algum lugar que posso encontrar o método na próxima vez que precisar!
Em relação às unidades, acho que nunca disse 'Kibibyte' ou 'Mebibyte' em minha vida, e embora eu seja cético em relação a esses padrões impostos em vez de desenvolvidos, acho que evitará confusão a longo prazo. .
fonte
Eu uso o método de extensão Long abaixo para converter em uma sequência de tamanho legível por humanos. Este método é a implementação em C # da solução Java desta mesma pergunta postada no Stack Overflow, aqui .
fonte