Formatee el número dado de bytes a un formato legible por humanos

16

Desafío y origen

En Stack Overflow, una pregunta popular es: ¿Cómo convertir el tamaño de byte en formato legible para humanos en Java? La respuesta más votada tiene un método bastante bueno para hacer esto, pero esto es codegolf y podemos hacerlo mejor, ¿no?

Su desafío es escribir un método o programa que convierta el número dado de bytes al formato legible por humanos correcto e imprima el resultado al estándar de su idioma. *

* ¡Vea las reglas para más aclaraciones!

Entrada

La entrada siempre será un número positivo de bytes con un máximo de (2 ^ 31) -1.

Salida

Puede elegir si prefiere el Sistema internacional de unidades o la notación binaria como salida (la notación SI probablemente le ahorre algunos bytes).

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

Nota: Las unidades superiores a GB o GiB no son posibles debido al rango de entrada restringido.

Salida de ejemplo

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

Binario:

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

Reglas

  • ¡Las funciones incorporadas para el formato de bytes no están permitidas!
  • La salida siempre debe estar en el mismo estándar de notación, no puede mezclar SI o binario;
  • La salida siempre debe estar en la unidad más grande posible donde el número resultante sea aún mayor o igual a uno;
  • La salida siempre debe tener un número decimal, pero puede elegir imprimir un número entero cuando la salida resultante esté en bytes (B);
  • Puede elegir si desea agregar un espacio, tabulación o nada entre el número y la unidad;
  • La entrada se recibe a través de STDIN o parámetros de función;
  • La salida se imprime en la consola o se devuelve como una cadena (o un contenedor de caracteres similar);
  • Este es el código de golf, por lo que gana la respuesta más corta. ¡Que te diviertas!

Editar: incluso más aclaraciones

Algunos números tienen comportamientos de redondeo interesantes como el número 999950. La mayoría de las implementaciones de código devolverían 1000.0 kB en lugar de 1.0 MB. ¿Por qué? Porque 999950/1000 se evalúa como 999.950, que se redondea efectivamente a 1000.0 cuando se utiliza String.format en Java (en la mayoría de los otros idiomas también). Hench necesita algunos controles adicionales para manejar casos como este.

Para este desafío, se aceptan ambos estilos, 1000.0 kB y 1.0 MB, aunque se prefiere el último estilo.

Pseudocódigo / código de prueba de 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 ツ
fuente
1
Técnicamente, los kilobytes SI deben usar kB(tenga en cuenta la minúscula k)
SuperJedi224
Buen punto, arreglado!
Rolf ツ
1
No quiero limitarme a mucho, por lo que diría que el espacio puede ser inconsistente. Pero con esta regla: la diferencia en el espacio y los caracteres de tabulación para diferentes entradas válidas no puede exceder 10. (Para mantener todo un poco "legible para humanos")
Rolf ツ
2
¿Cuál es el resultado esperado para 999999y 1000000? 160581exhibe redondeo, entonces debería ser 1000.0kBy 1.0MB?
Sp3000
3
@ Sp3000 Esa es una buena pregunta, la mejor solución sería que 999999 muestre 1.0 MB. Pero para este desafío, diría que 1000.0 KB y casos similares de redondeo también están bien.
Rolf ツ

Respuestas:

10

TI-BASIC, 44

Sería la herramienta adecuada para el trabajo si TI-BASIC tuviera una manipulación de cadena decente (tenía que recurrir a sobrescribir el exponente del número, que se muestra en notación de ingeniería, con la unidad). Como es, se redondea y sale correctamente, pero ni siquiera está cerca de las entradas ganadoras. ¿Quizás un lenguaje de calculadora diferente podría ganar este?

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

Ingrese el formulario [number]:[program name]en la pantalla de inicio.

Dados casos de prueba:

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
fuente
No tenía ni idea de que TI-BASIC fuera tan versátil jaja
Beta Decay
1
TI-BASIC no es versátil, pero a menudo hay soluciones extrañas para algunas de sus deficiencias.
lirtosiast
6

CJam, 35 27 bytes

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

Gracias Dennis por eliminar 8 bytes.

Esto no se imprime .0en el intérprete en línea . Pero como Dennis ha señalado , funciona bien en el intérprete de Java.

Explicación

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
fuente
ri{_e-3XmO_i}g;o]," kMG"='B(27 bytes)
Dennis
@ Dennis Gracias por el 1mO. Pero este código no funciona para 1149999...
jimmy23013
ri{_e-3_i}g;1mOo]," kMG"='Bdebería.
Dennis
Rasca eso, eso tiene otros errores.
Dennis
999999se convierte 1000kB. Al leer la pregunta nuevamente, no estoy seguro de si 1000kBrealmente estaría mal.
Dennis
5

Pyth 29 27 bytes

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

Demostración. Arnés de prueba.

Explicación:

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
fuente
3

CJam, 28

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

Pruébalo en línea

Nota: no muestra ".0" con el intérprete en línea, pero sí con el intérprete oficial de Java .

Explicación:

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
fuente
¿Para qué sirve el backtick?
Dennis
@Dennis mostrando .0 en el intérprete en línea
aditsu
Funciona bien en el intérprete de Java sin la tecla de retroceso, por lo que no creo que lo necesite.
Dennis
3

Python 2 - 76 bytes

Utiliza el Sistema Internacional de Unidades, simplemente porque es más fácil de hacer en tu cabeza;)

n=input();m=0;f=1e3
while n>=f:n/=f;m+=2
print"%.1f%s"%(n,'B kBMBGB'[m:m+2])
Decaimiento Beta
fuente
no me parece bien, no respeta el formato solicitado, por ejemplo, si envío "2147483647" obtengo "2.000000GB" - La pregunta pide un decimal y quizás un espacio.
Dieter
1
Además, esto es 79 bytes de acuerdo con esto . Esto es 75 bytes. No creo que se haya especificado que debe haber un espacio entre el número y la unidad.
Kade
puede guardar un byte conf=1e3
mbomb007
@ mbomb007 En realidad, ahorró 2 bytes porque 1e3 es flotante
Decaimiento Beta
Sabía que era una carroza. Supongo que no puedo 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
fuente
2

Haskell, 119

Lamentablemente, no pude encontrar un camino más corto en Haskell para asegurar 1 lugar decimal en las carrozas, pero estoy publicando para la posteridad.

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"

Versión moderadamente menos golfizada:

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
fuente
2

Java, 106 bytes

Este es un método que toma un número y devuelve una cadena.

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
fuente
Se le permite programar una función que devuelve una cadena en lugar de un programa completo, puede ahorrarle algunos bytes;)
Rolf ツ
Tres cosas: si está convirtiendo a a doble de todos modos (no sé si es necesario), puede usar 1e3para 1000; puedes convertir eso while()a for()ay usar los puntos y comas libres; y no sé si esto funciona porque parece mostrar todos los dígitos decimales, no solo uno más allá del decimal.
lirtosiast
@ThomasKwa: La última vez que lo comprobé, la pregunta no parecía especificarlo explícitamente. Pero supongo que lo hace ahora.
SuperJedi224
1

Python 2, 127 bytes

Usando la ISU. El fragmento declara una función 'C' que toma el número a convertir 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'

Algún código de prueba:

    print 'Input\tOutput'
for v in [0,999,1000,1023,1023,1601,160581,4066888,634000000,2147483647]:
 print v,C(v)
dieter
fuente
Puede usar en 1e3lugar de1000.0
mbomb007
1

JavaScript ( ES6 ), 71

Usar unidades SI: una función que devuelve la cadena solicitada.

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

Este más corto sigue las reglas, particularmente 3 y 4

  • La salida siempre debe estar en la unidad más grande posible donde el número resultante sea aún mayor o igual a uno, entonces 995 => 1.0kB
  • La salida siempre debe tener un número decimal, pero puede elegir imprimir un número entero cuando la salida resultante esté en bytes (B) Elijo no, así que 10 => 10.0 B

Por desgracia, de esta manera, los resultados no coinciden con los ejemplos.

Para que coincida con los ejemplos, aquí hay uno más largo, en mayúsculas especiales para números pequeños (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)

Ejecute el fragmento para probar (siendo EcmaScript 6, solo Firefox)

edc65
fuente
1

Python, 61 bytes

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

Llama como f(999). Tenga en cuenta que 1e3es flotante, por lo que funciona tanto con Python 2 como con Python 3.

Sp3000
fuente
1

PHP4.1, 63 62 bytes

No es el mejor golf, pero seguramente es bastante corto.

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

Para usarlo, acceda a través de POST / GET o configure un valor en la SESIÓN, en la tecla B.

Deje la llave sin Iconfigurar!

Ismael Miguel
fuente
1

SpecBAS - 100 bytes

Usando la convención ISU.

Me di cuenta de que tener una variable establecida en 1e3 (que necesita una instrucción LET para asignarla), y luego usar esa variable en el ejercicio, en realidad usaba más caracteres que simplemente codificar el 1e3 donde era necesario.

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

Lo hice a lo largo, esto es bastante malo.

Salida

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

TB agregada para 39 bytes adicionales

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}

Salida:

c[1000000000000] # => "1.0TB"
La fuerza bruta
fuente
1

Sed -r, 218 + 1

Estoy usando unidades SI; Creo que elegir unidades binarias sería una política valiente . ;-)

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

Reformateado:

#!/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/./

Salida

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

Variaciones

Las reglas parecen implicar redondear al más cercano, pero para la visualización humana, creo que redondear hacia abajo es una alternativa aceptable y ahorra 123 bytes (mejor que el 50%):

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

La extensión natural a unidades más grandes (aún redondeando hacia abajo, 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/

Salida de variación:

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
fuente
¡Gran trabajo! ¡Me gusta que hayas pensado en todas las diferentes opciones!
Rolf ツ
1

C, 77 75

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

Esto usa unidades SI y toma la opción 1000.0kB para redondear.

Código ampliado:

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

Salida

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 obtener unidades binarias, el cambio 1000a 1024, y añadir ia la cadena de formato si hay un multiplicador. Para evitar el redondeo de 4 dígitos, compare en >=.95lugar de >=1. Para aceptar unidades más grandes, extienda la ucadena. Combinando todas estas opciones, obtenemos:

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

Salida 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 prueba

Pase cualquier número de entradas como argumentos de línea 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
fuente
Nice one;) Bien ejecutado!
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]}"

Probablemente podría hacerlo un poco mejor si me esforzara más, pero esto es lo que tengo hasta ahora.

David Bailey
fuente
Usar en 1024.lugar de 1024.0.
mbomb007
0

Javascript ES5, 69 bytes

Esto utiliza una forma diferente de alcanzar el objetivo final que la respuesta de @ edc65 .
En realidad, esto está bastante cerca de mi respuesta PHP .

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

Simplemente ejecute el fragmento de pila o péguelo en su consola.

Ismael Miguel
fuente
0

Rubí, 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
fuente