Formate o número de bytes especificado para um formato legível por humanos

16

Desafio e origem

No Stack Overflow, uma pergunta popular é: Como converter o tamanho de bytes em formato legível por humanos em java? A resposta mais votada tem um método bastante legal para fazer isso, mas esse é um codegolf e podemos fazer melhor, não podemos?

Seu desafio é escrever um método ou programa que cubra o número especificado de bytes no formato legível humano correto e imprima o resultado no padrão do seu idioma. *

* Veja as regras para mais esclarecimentos!

Entrada

A entrada sempre será um número positivo de bytes com um máximo de (2 ^ 31) -1.

Resultado

Você pode escolher se prefere o Sistema Internacional de Unidades ou a notação binária como saída (a notação SI provavelmente economiza alguns bytes).

SI:      B, kB,  MB,  GB  
Binary:  B, KiB, MiB, GiB

Nota: Unidades superiores a GB ou GiB não são possíveis devido ao intervalo de entrada restrito.

Saída de exemplo

Sistema Internacional de Unidades:

Input       Output
0           0.0     B
999         999.0   B
1000        1.0     kB
1023        1.0     kB
1024        1.0     kB
1601        1.6     kB
160581      160.6   kB
4066888     4.1     MB
634000000   634.0   MB
2147483647  2.1     GB

Binário:

Input       Output
0           0.0     B
999         999.0   B
1000        1000.0  B
1023        1023.0  B
1024        1.0     KiB
1601        1.6     KiB
160581      156.8   KiB
4066888     3.9     MiB
634000000   604.6   MiB
2147483647  2.0     GiB

Regras

  • Funções internas para formatação de bytes não são permitidas!
  • A saída deve sempre estar no mesmo padrão de notação; você não pode misturar SI ou binário;
  • A saída deve sempre estar na maior unidade possível, onde o número resultante ainda é maior ou igual a um;
  • A saída sempre deve ter um número decimal, mas você pode optar por imprimir um número inteiro quando a saída resultante estiver em bytes (B);
  • Você pode escolher se deseja adicionar um espaço, tabulação ou nada entre o número e a unidade;
  • A entrada é recebida via STDIN ou parâmetros de função;
  • A saída é impressa no console ou retornada como string (ou contêiner de caractere semelhante);
  • Isso é código de golfe, então a resposta mais curta vence. Diverta-se!

Edit: Esclarecimentos ainda mais

Alguns números têm comportamentos interessantes de arredondamento, como o número 999950. A maioria das implementações de código retornaria 1000,0 kB em vez de 1,0 MB. Por quê? Porque 999950/1000 é avaliado para 999.950, que é efetivamente arredondado para 1000.0 ao usar String.format em Java (na maioria das outras linguagens também). Hench algumas verificações extras são necessárias para lidar com casos como este.

Para esse desafio, os dois estilos são aceitos 1000,0 kB e 1,0 MB, embora o último estilo seja o preferido.

Pseudo-código / código de teste java:


public static String bytesToSI(long bytes){
      if (bytes < 1000){
          return bytes + ".0 B";
      }
      //Without this rounding check:
      //999950    would be 1000.0 kB instead of 1.0 MB
      //999950000 would be 1000.0 MB instead of 1.0 GB
      int p = (int) Math.ceil(Math.log(bytes) / Math.log(1000));
      if(bytes/Math.pow(1000, p) < 0.99995){
          p--;
      }
      //Format
      return String.format("%.1f %sB", bytes/Math.pow(1000, p), "kMGTPE".charAt(p-1));
}

Rolf ツ
fonte
11
Tecnicamente, kilobytes SI deve usar kB(note a minúscula k)
SuperJedi224
Bom ponto, fixo!
Rolfツ
11
Como não quero limitar muito, diria que o espaçamento pode ser inconsistente. Mas com esta regra: A diferença no espaço e guia caracteres para diferentes entradas válidas não pode exceder 10. (Para manter tudo um pouco "legível")
Rolfツ
2
Qual é o resultado esperado para 999999e 1000000? 160581exibe arredondamento, então deveria ser 1000.0kBe 1.0MB?
SP3000
3
@ Sp3000 Essa é uma boa pergunta, a melhor solução seria que o 999999 exibisse 1,0 MB. Mas para este desafio, eu diria que 1000,0 KB e casos de arredondamento semelhantes também são bons.
Rolfツ

Respostas:

10

TI-BASIC, 44

Seria a ferramenta certa para o trabalho se o TI-BASIC tivesse uma manipulação decente de cadeias pela metade (eu tive que recorrer à substituição do expoente do número, exibido em notação de engenharia, com a unidade). Como é, arredonda e produz corretamente, mas não chega nem perto de ganhar entradas. Talvez um idioma diferente da calculadora possa ganhar este?

Fix 1
Eng
ClrHome
Disp Ans
Output(1,15,sub(" kMG",1+iPart(log(Ans+.5)/3),1)+"B

Entrada no formulário [number]:[program name] na tela inicial.

Casos de teste dados:

Input       Output (leading spaces intentional; screen clear before each output)
0                      0.0 B
999                  999.0 B
1000                   1.0kB
1023                   1.0kB
1024                   1.0kB
1601                   1.6kB
160581               160.6kB
4066888                4.1MB
634000000            634.0MB
2147483647             2.1GB
lirtosiast
fonte
Eu não tinha absolutamente nenhuma ideia de que a TI-BASIC foi tão versátil haha
Beta Decay
11
O TI-BASIC não é versátil, mas muitas vezes existem soluções alternativas estranhas para algumas de suas deficiências.
precisa saber é o seguinte
6

CJam, 35 27 bytes

ri{_e-3_i}g;1mOo]," kMG"='B

Obrigado Dennis por remover 8 bytes.

Isso não é impresso .0no intérprete online . Mas como Dennis apontou , funciona bem no interpretador Java.

Explicação

ri         e# Read the input as an integer.
{          e# Do:
    _e-3   e#   Make a copy and divide by 1000.
           e#   This will generate one more item in the stack for each iteration.
    _i     e#   Make a copy and truncate to integer.
}g         e# until the integer part is 0.
;          e# Discard the final value with integer part 0.
1mOo       e# Output the number before it with the correct format.
],         e# Count the number of iterations - 1.
" kMG"=    e# Select a character according to the number of iterations.
'B         e# Output B.
jimmy23013
fonte
ri{_e-3XmO_i}g;o]," kMG"='B(27 bytes)
Dennis
@Dennis Obrigado pela 1mO . Mas este código não funciona para 1149999...
jimmy23013
ri{_e-3_i}g;1mOo]," kMG"='Bdevemos.
Dennis
Risque isso, que tem outros bugs.
Dennis
999999torna-se 1000kB. Lendo a pergunta novamente, não tenho certeza se 1000kBrealmente estaria errado.
Dennis
5

Pitão, 29 27 bytes

p@" kMG"Js.lQK^T3.RcQ^KJ1\B

Demonstração. Equipamento de teste.

Explicação:

p@" kMG"Js.lQK^T3.RcQ^KJ1\B
                                 Implicit: Q = eval(input())
p                                print, in the order 2nd arg then 1st arg:
             K^T3                K = 10^3 = 1000
          .lQK                   log of Q base K
         s                       Floored
        J                        Store to J
 @" kMG"J                        The Jth character of ' kMG'
                     ^KJ         K^J
                   cQ            Q/K^J (Floating point division)
                 .R     1        Round to 1 decimal place.
                         \B      Print a trailing 'B'.
isaacg
fonte
3

CJam, 28

r_dA@,(3/:X3*#/1mO" kMG"X='B

Experimente online

Nota: não mostra ".0" com o intérprete online, mas o faz com o interpretador java oficial .

Explicação:

r_          read and duplicate
dA          convert to double and push 10
@           bring the initial string to the top
,(          get the length and decrement
3/          divide by 3 (for thousands)
:X3*        store in X and multiply by 3 again
#           raise 10 to that power
/           divide the original number by it
1mO         round to 1 decimal
" kMG"X=    convert X from 0/1/2/3 to space/k/M/G
'B          add a 'B'
aditsu
fonte
Para que serve o bastão?
Dennis
@Dennis mostrando .0 no intérprete on-line
aditsu
Funciona bem no interpretador Java sem o backtick, então não acho que você precise.
Dennis
3

Python 2 - 76 bytes

Usa o Sistema Internacional de Unidades, simplesmente porque é mais fácil de fazer na sua cabeça;)

n=input();m=0;f=1e3
while n>=f:n/=f;m+=2
print"%.1f%s"%(n,'B kBMBGB'[m:m+2])
Beta Decay
fonte
parece não estar correto para mim, não respeita a formatação solicitada; por exemplo, se eu enviar "2147483647", obtenho "2.000000GB" - a pergunta pede um decimal e talvez um espaço.
Dieter
11
Além disso, são 79 bytes de acordo com isso . Isso é 75 bytes. Não acredito que tenha sido especificado que precisa haver um espaço entre o número e a unidade.
Kade
você pode salvar um byte com #f=1e3
mbomb007
@ mbomb007 Na verdade, salvo 2 bytes porque 1e3 é uma bóia
Beta Decay
Eu sabia que era um carro alegórico. Eu acho que eu não posso contar ...
mbomb007
2

POWERSHELL, 190

$x=Read-Host
function f($a,$b){"$x`t"+[math]::Round($x/$a,1).ToString("F1")+"`t$b"}
if(1KB-gt$x){f 1 "B"}elseif(1MB-gt$x){f 1KB KiB}
elseif(1GB-gt$x){f 1MB MiB}elseif(1TB-gt$x){f 1GB GiB}

uso

PS C:\> .\makehum.ps1
1601
1601    1.6     KiB
PS C:\> .\makehum.ps1
4066888
4066888 3.9     MiB
PS C:\> .\makehum.ps1
160581
160581  156.8   KiB
PS C:\> .\makehum.ps1
634000000
634000000       604.6   MiB
PS C:\> .\makehum.ps1
2147483647
2147483647      2.0     GiB
PS C:\>
blabb
fonte
2

Haskell, 119

Infelizmente, não consegui encontrar uma maneira mais curta em Haskell para garantir uma casa decimal em carros alegóricos, mas estou postando para a posteridade.

import Text.Printf
a#n|p>=1=(a+1)#p|1<2=(a,n)where p=n/1000
m n=let(a,b)=0#n in printf"%.1f"b++["B","kB","MB","GB"]!!a

Uso:

> m 160581
"160.6kB"

Versão menos golfe:

import Text.Printf

countThousands :: Int -> Float -> (Int, Float)
countThousands count num
 |nextNum >= 1 = countThousands (count+1) nextNum
 |otherwise    = (count,num)
 where nextNum = num/1000

printHuman :: Float -> String
printHuman n = let (a,b) = countThousands 0 n in 
  (printf "%.1f" b) ++ (["B","kB","MB","GB"]!!a)
Craig Roy
fonte
2

Java, 106 bytes

Esse é um método que pega um número e retorna uma string.

String f(int n){int k=0;for(;n>1e3;k++)n/=1e3;return(int)(10*n)/10.0+new String[]{"","k","M","G"}[k]+"B";}
SuperJedi224
fonte
Você está autorizado a programar uma função a devolver uma cadeia em vez de um programa completo, pode poupar alguns bytes;)
Rolfツ
Três coisas: se você estiver convertendo um para o dobro (não sei se é necessário), você pode usá 1e3-lo 1000; você pode convertê-lo while()em a for()e usar o ponto e vírgula livre; e não sei se isso funciona porque parece exibir todos os dígitos decimais, não apenas um após a casa decimal.
lirtosiast
@ ThomasKwa: Última vez que verifiquei, a pergunta não parecia especificar isso explicitamente. Mas acho que sim agora.
SuperJedi224
1

Python 2, 127 bytes

Usando o ISU. O trecho declara uma função 'C' que leva o número a ser convertido como argumento.

C=lambda v:min(['%.1f %sB'%(x,u)for x,u in[(v/1000.0**i,'bkMG'[i])for i in range(4)]if x>=1]).replace('.0 b',' ')if v else'0 B'

Algum código de teste:

    print 'Input\tOutput'
for v in [0,999,1000,1023,1023,1601,160581,4066888,634000000,2147483647]:
 print v,C(v)
dieter
fonte
Você pode usar em 1e3vez de1000.0
mbomb007 22/06
1

JavaScript ( ES6 ), 71

Usando unidades SI - Uma função retornando a sequência solicitada.

f=(a,b=3)=>+(r=eval('a/1e'+b*3).toFixed(1))[0]?r+' kMG'[b]+'B':f(a,b-1)

Este menor segue as regras, particularmente 3 e 4

  • A saída deve sempre estar na maior unidade possível, onde o número resultante ainda é maior ou igual a um, em seguida, 995 => 1,0kB
  • A saída sempre deve ter um número decimal, mas você pode optar por imprimir um número inteiro quando a saída resultante estiver em bytes (B) que eu escolher, então 10 => 10,0 B

Infelizmente, desta forma, os resultados não correspondem aos exemplos.

Para corresponder aos exemplos, aqui está um mais longo, especial para números pequenos (82 bytes)

f=(a,b=3)=>a<1e3?a+'B':+(r=eval('a/1e'+b--*3).toFixed(1))[0]?r+'kMG'[b]+'B':f(a,b)

Execute o snippet para testar (sendo apenas EcmaScript 6, Firefox)

edc65
fonte
1

Python, 61 bytes

f=lambda n,i=0:"%.1f%cB"%(n," kMG"[i])*(n<1e3)or f(n/1e3,i+1)

Ligue como f(999). Observe que 1e3é um flutuador, portanto, isso funciona com o Python 2 e o Python 3.

Sp3000
fonte
1

PHP4.1, 63. 62 bytes

Não é o melhor golfe, mas certamente é bastante curto.

<?for($S=kMG;$B>1e3;$I++)$B/=1e3;printf("%.1f{$S[$I-1]}B",$B);

Para usá-lo, acesse através de POST / GET ou defina um valor em SESSION, na tecla B.

Deixe a chave Idesabilitada!

Ismael Miguel
fonte
1

SpecBAS - 100 bytes

Usando a convenção ISU.

Percebi que ter uma variável definida como 1e3 (que precisa de uma instrução LET para atribuí-la) e, em seguida, usar essa variável na elaboração, na verdade, usava mais caracteres do que apenas codificar o 1e3 onde era necessário.

1 INPUT n: LET i=1
2 DO WHILE n>1e3: LET n=n/1e3: INC i: LOOP 
3 PRINT USING$("&.*0#",n);" kMG"(i);"B"
Brian
fonte
1

Ruby, 128 bytes

c=->i{p i.to_s+'B'if i<1e3;p (i/1e3).to_s+'kB'if i>=1e3&&i<1e6;p (i/1e6).to_s+'MB'if i>=1e6&&i<1e9;p (i/1e9).to_s+'GB'if i>=1e9}

Eu fiz o longo caminho, isso é muito ruim.

Resultado

c[0] # => "0B"
c[999] # => "999B"
c[1000] # => "1.0kB" 
c[1023] # => "1.023kB"
c[1024] # => "1.024kB"
c[1601] # => "1.601kB"
c[160581] # => "160.581kB"
c[4066888] # => "4.066888MB"
c[634000000] # => "634.0MB"
c[2147483647] # => "2.147483647GB"

Editar

Adicionada TB por 39 bytes extras

c=->i{p i.to_s+'B'if i<1e3;p (i/1e3).to_s+'kB'if i>=1e3&&i<1e6;p (i/1e6).to_s+'MB'if i>=1e6&&i<1e9;p (i/1e9).to_s+'GB'if i>=1e9&&i<1e12;p (i/1e12).to_s+'TB'if i>=1e12}

Resultado:

c[1000000000000] # => "1.0TB"
Sheerforce
fonte
1

Sed -r, 218 + 1

Eu estou usando unidades SI; Eu acho que escolher unidades binárias seria uma política corajosa . ;-)

s/(.)((...)+)$/\1z\2/;h;s/[^z]*z?//;s/.../k/g;s/kk/M/;s/Mk/G/;x;s/(z.)[5-9].*/\1c/;s/(z.c?).*/\1/;:;s/9c/c0/;s/zc/cz/;t;s/(^|0)c/1/;s/1c/2/;s/2c/3/;s/3c/4/;s/4c/5/;s/5c/6/;s/6c/7/;s/7c/8/;s/8c/9/;G;s/\n//;s/$/B/;y/z/./

Reformatado:

#!/bin/sed -rf

# Place decimal point (use z as shorthand for \.)
s/(.)((...)+)$/\1z\2/
h

# count thousands into hold space
s/[^z]*z?//
s/.../k/g
s/kk/M/;s/Mk/G/
x

# truncate to 1 decimal place
s/(z.)[5-9].*/\1c/
s/(z.c?).*/\1/

# propagate carry
:
s/9c/c0/
s/zc/cz/
t
s/(^|0)c/1/
s/1c/2/
s/2c/3/
s/3c/4/
s/4c/5/
s/5c/6/
s/6c/7/
s/7c/8/
s/8c/9/

# Append units
G;s/\n//
s/$/B/
y/z/./

Resultado

1 => 1B
9 => 9B
99 => 99B
999 => 999B
1000 => 1.0kB
9999 => 10.0kB
99949 => 99.9kB
99950 => 100.0kB
99999 => 100.0kB
999999 => 1000.0kB
9999999 => 10.0MB
9999999999 => 10.0GB
1000 => 1.0kB
10000 => 10.0kB
10005 => 10.0kB
10440 => 10.4kB
10450 => 10.5kB
10950 => 11.0kB

Variações

As regras parecem implicar arredondar para o mais próximo, mas para exibição humana, acredito que o arredondamento é uma alternativa aceitável e salva 123 bytes (melhor que 50%):

s/(.)((...)+)$/\1.\2/;h;s/[^\.]*\.?//;s/.../k/g;s/kk/M/;s/Mk/G/;x;s/(\..).*/\1/;G;s/\n//;s/$/B/

A extensão natural para unidades maiores (ainda arredondando para baixo, 130 + 1 bytes):

s/(.)((...)+)$/\1.\2/;h;s/[^\.]*\.?//;s/.../k/g;s/kk/M/g;s/Mk/G/;s/MM/T/g;s/TT/Y/;s/TM/E/;s/TG/Z/;x;s/(\..).*/\1/;G;s/\n//;s/$/B/

Saída de variação:

1 => 1B
9 => 9B
99 => 99B
999 => 999B
1000 => 1.0kB
9999 => 9.9kB
99949 => 99.9kB
99950 => 99.9kB
99999 => 99.9kB
999999 => 999.9kB
9999999 => 9.9MB
9999999999 => 9.9GB
1000 => 1.0kB
10000 => 10.0kB
10005 => 10.0kB
10440 => 10.4kB
10450 => 10.4kB
10950 => 10.9kB
1000000000 => 1.0GB
1000000000000 => 1.0TB
1000000000000000 => 1.0MGB
1000000000000000000 => 1.0EB
1000000000000000000000 => 1.0ZB
1000000000000000000000000 => 1.0YB
999999999999999999999999999 => 999.9YB
Toby Speight
fonte
Bom trabalho! Eu gosto que você tenha pensado em todas as diferentes opções!
Rolfツ
1

C, 77 75

f(float l){char*u=" kMG";while((l/=1e3)>=1)++u;printf("%.1f%cB",l*1e3,*u);}

Isso usa unidades SI e aceita a opção 1000.0kB para arredondamento.

Código expandido:

f(float l)
{
    char *u = " kMG";
    while ((l/=1000) >= 1)
        ++u;
    printf("%.1f%cB", l*1000, *u);
}

Resultado

9 => 9.0 B
9999 => 10.0kB
1023 => 1.0kB
1024 => 1.0kB
999990 => 1000.0kB
1048575 => 1.0MB
1048576 => 1.0MB
2147483647 => 2.1GB

Variantes

Para obter unidades binárias, altere 1000para 1024e adicione ià string de formato, se houver um multiplicador. Para evitar o arredondamento de quatro dígitos, compare em >=.95vez de >=1. Para aceitar unidades maiores, estenda a ucorda. Combinando todas essas opções, obtemos:

f(float l)
{
    char*u=" kMGTPEZY";
    while((l/=1024)>=.95)++u;
    printf(*u-' '?"%.1f%ciB":"%.0fB",l*1024,*u);
}

Saída variante

9 => 9B
9999 => 9.8kiB
1023 => 1.0kiB
1024 => 1.0kiB
999990 => 1.0MiB
1048575 => 1.0MiB
1048576 => 1.0MiB
2147483647 => 2.0GiB
1000000000 => 953.7MiB
1000000000000 => 931.3GiB
1000000000000000 => 909.5TiB
1000000000000000000 => 888.2PiB
1000000000000000000000 => 867.4EiB
1000000000000000000000000 => 847.0ZiB
999999999999999999999999999 => 827.2YiB
1176043059457204080886151645 => 972.8YiB

Programa de teste

Passe qualquer número de entradas como argumentos da linha de comandos:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    while (*++argv) {
        printf("%s => ", *argv);
        f(strtod(*argv, 0));
        puts("");
    }
    return 0;
}
Toby Speight
fonte
Bom;) Bem executado!
Rolfツ
0

Ruby, 91 bytes

n=gets.to_i;i=0;while n>1023;n/=1024.0;i+=1;end;puts "#{n.round 1} #{%w[B KiB MiB GiB][i]}"

Eu provavelmente poderia me sair um pouco melhor se tentasse mais, mas aqui está o que tenho até agora.

David Bailey
fonte
Use em 1024.vez de 1024.0.
mbomb007
0

Javascript ES5, 69 bytes

Isso usa uma maneira diferente de alcançar a meta final que a resposta da @ edc65 .
Na verdade, isso é bem parecido com a minha resposta PHP .

for(i=+prompt(z=0);i>1e3;z++)i/=1e3;alert(i.toFixed(1)+' kMG'[z]+'B')

Simplesmente execute o snippet da pilha ou cole-o no seu console.

Ismael Miguel
fonte
0

Ruby, 90 bytes

proc{|n|q=((1..3).find{|i|n<(1<<i*10)}||4)-1;[n*10/(1<<q*10)/10.0,%w[B kB MB GB][q]].join}
csabahenk
fonte