Mettre en forme le nombre d'octets donné dans un format lisible par l'homme


16

Défi et origine

Sur Stack Overflow, une question populaire est: Comment convertir la taille des octets en format lisible par l'homme en Java? La réponse la plus votée a une assez bonne méthode pour le faire, mais c'est du codegolf et nous pouvons faire mieux, n'est-ce pas?

Votre défi est d'écrire une méthode ou un programme qui couvre le nombre d'octets donné au format lisible par l'homme et imprime le résultat au standard dans votre langue. *

* Voir les règles pour plus de précisions!

Contribution

L'entrée sera toujours un nombre d'octets positif avec un maximum de (2 ^ 31) -1.

Production

Vous pouvez choisir si vous préférez le système international d'unités ou la notation binaire en sortie (la notation SI vous fait probablement économiser quelques octets).

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

Remarque: des unités supérieures à GB ou GiB ne sont pas possibles en raison de la plage d'entrée restreinte.

Exemple de sortie

Système international d'unités:

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

Binaire:

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

Règles

  • Les fonctions intégrées de formatage d'octets ne sont pas autorisées!
  • La sortie doit toujours être dans la même norme de notation, vous ne pouvez pas mélanger SI ou binaire;
  • La sortie doit toujours être dans la plus grande unité possible lorsque le nombre résultant est toujours supérieur ou égal à un;
  • La sortie doit toujours avoir un nombre décimal, mais vous pouvez choisir d'imprimer un nombre entier lorsque la sortie résultante est en octets (B);
  • Vous pouvez choisir si vous souhaitez ajouter un espace, une tabulation ou rien entre le nombre et l'unité;
  • L'entrée est reçue via STDIN ou les paramètres de fonction;
  • La sortie est imprimée sur la console ou retournée sous forme de chaîne (ou conteneur de caractères similaire);
  • C'est le golf de code, donc la réponse la plus courte l'emporte. S'amuser!

Edit: Encore plus de clarification

Certains nombres ont des comportements d'arrondi intéressants comme le nombre 999950. La plupart des implémentations de code renverraient 1000,0 ko au lieu de 1,0 Mo. Pourquoi? Parce que 999950/1000 est évalué à 999.950, ce qui est effectivement arrondi à 1000.0 lors de l'utilisation de String.format en Java (dans la plupart des autres langues également). Des contrôles supplémentaires sont nécessaires pour gérer des cas comme celui-ci.

Pour ce défi, les deux styles, 1000,0 ko et 1,0 Mo sont acceptés, bien que le dernier style soit préféré.

Pseudo code / code de test 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));
}


1
Techniquement, les kilo-octets SI devraient utiliser kB(notez les k minuscules)
SuperJedi224

Bon point, corrigé!
Rolf

1
Je ne veux pas limiter trop, donc je dirais que l'espacement peut être incohérent. Mais avec cette règle: la différence d'espace et de tabulations pour différentes entrées valides ne peut pas dépasser 10. (Pour que tout reste un peu "lisible par l'homme")
Rolf ツ

2
Quelle est la sortie attendue pour 999999et 1000000? 160581les expositions doivent être arrondies, devrait-il en être 1000.0kBainsi 1.0MB?
Sp3000

3
@ Sp3000 C'est une bonne question, la meilleure solution serait que 999999 affiche 1,0 Mo. Mais pour ce défi, je dirais que 1000,0 Ko et des cas d'arrondi similaires conviennent également.
Rolf

Réponses:


10

TI-BASIC, 44

Ce serait le bon outil pour le travail si TI-BASIC avait une manipulation de chaîne à moitié décente (j'ai dû recourir à l'écrasement de l'exposant du nombre, affiché en notation d'ingénierie, avec l'unité). En l'état, il arrondit et sort correctement, mais ce n'est même pas proche des entrées gagnantes. Peut-être qu'une autre langue de calculatrice pourrait gagner celle-ci?

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

Entrez dans le formulaire [number]:[program name]sur l'écran d'accueil.

Étant donné les cas de test:

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

Je n'avais absolument aucune idée que TI-BASIC était si polyvalent haha
Beta Decay

1
TI-BASIC n'est pas polyvalent, mais il existe souvent d'étranges solutions de contournement pour certains de ses défauts.
lirtosiast

6

CJam, 35 27 octets

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

Merci Dennis d'avoir supprimé 8 octets.

Cela ne s'imprime pas .0dans l' interpréteur en ligne . Mais comme Dennis l'a souligné , cela fonctionne très bien dans l'interpréteur Java.

Explication

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.

ri{_e-3XmO_i}g;o]," kMG"='B(27 octets)
Dennis

@Dennis Merci pour le 1mO. Mais ce code ne fonctionne pas pour 1149999...
jimmy23013

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

Grattez ça, ça a d'autres bugs.
Dennis

999999devient 1000kB. En relisant la question, je ne sais pas si ce 1000kBserait vraiment faux.
Dennis

5

Pyth, 29 27 octets

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

Manifestation. Harnais de test.

Explication:

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

3

CJam, 28

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

Essayez-le en ligne

Remarque: il ne montre pas ".0" avec l'interpréteur en ligne, mais le fait avec l' interpréteur java officiel .

Explication:

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'

À quoi sert le backtick?
Dennis

@Dennis montrant .0 dans l'interprète en ligne
aditsu

Cela fonctionne bien dans l'interpréteur Java sans backtick, donc je ne pense pas que vous en ayez besoin.
Dennis

3

Python 2 - 76 octets

Utilise le système international d'unités, simplement parce que c'est plus facile à faire dans votre tête;)

n=input();m=0;f=1e3
while n>=f:n/=f;m+=2
print"%.1f%s"%(n,'B kBMBGB'[m:m+2])

cela ne me semble pas correct, cela ne respecte pas la mise en forme demandée, par exemple si je soumets "2147483647" j'obtiens "2.000000GB" - La question demande une décimale, et peut-être un espace.
dieter

1
En outre, c'est 79 octets selon cela . C'est 75 octets. Je ne pense pas qu'il ait été spécifié qu'il doit y avoir un espace entre le numéro et l'unité.
Kade

vous pouvez enregistrer un octet avecf=1e3
mbomb007

@ mbomb007 En fait, il a économisé 2 octets car 1e3 est un flottant
Beta Decay

Je savais que c'était un flotteur. Je suppose que je ne peux pas compter ...
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}

usage

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:\>

2

Haskell, 119

Je n'ai malheureusement pas trouvé un moyen plus court à Haskell pour assurer 1 décimale dans les flotteurs, mais je poste pour la postérité.

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

Usage:

> m 160581
"160.6kB"

Version modérément moins golfée:

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)

2

Java, 106 octets

Celui-ci est une méthode qui prend un nombre et renvoie une chaîne.

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

Vous êtes autorisé à programmer une fonction renvoyant une chaîne au lieu d'un programme complet, cela peut vous faire économiser quelques octets;)
Rolf

Trois choses: si vous convertissez de toute façon un double (je ne sais pas si c'est nécessaire), vous pouvez utiliser 1e3pour 1000; vous pouvez le convertir while()en a for()et utiliser les points-virgules gratuits; et je ne sais pas si cela fonctionne, car il semble afficher tous les chiffres décimaux, pas seulement un après la décimale.
lirtosiast

@ThomasKwa: La dernière fois que j'ai vérifié, la question ne semblait pas le spécifier explicitement. Mais je suppose que c'est le cas maintenant.
SuperJedi224

1

Python 2, 127 octets

Utilisation de l'ISU. L'extrait déclare une fonction «C» qui prend le nombre à convertir en argument.

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'

Un code de test:

    print 'Input\tOutput'
for v in [0,999,1000,1023,1023,1601,160581,4066888,634000000,2147483647]:
 print v,C(v)

Vous pouvez utiliser 1e3au lieu de1000.0
mbomb007

1

JavaScript ( ES6 ), 71

Utilisation des unités SI - Une fonction renvoyant la chaîne demandée.

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

Celui-ci plus court suit les règles, en particulier 3 et 4

  • La sortie doit toujours être dans la plus grande unité possible lorsque le nombre résultant est toujours supérieur ou égal à un puis 995 => 1,0 Ko
  • La sortie doit toujours avoir un nombre décimal, mais vous pouvez choisir d'imprimer un nombre entier lorsque la sortie résultante est en octets (B) je choisis de ne pas, donc 10 => 10,0 B

Hélas, de cette façon, les résultats ne correspondent pas aux exemples.

Pour correspondre aux exemples, voici un plus long, boîtier spécial pour les petits nombres (82 octets)

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

Exécutez l'extrait de code pour tester (étant EcmaScript 6, Firefox uniquement)


1

Python, 61 octets

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

Appelez comme f(999). Notez que 1e3c'est un flottant, donc cela fonctionne avec Python 2 et Python 3.


1

PHP4.1, 63 62 octets

Pas le meilleur golf, mais c'est sûrement assez court.

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

Pour l'utiliser, accédez via POST / GET ou définissez une valeur dans la SESSION, sur la clé B.

Laissez la clé Inon réglée!


1

SpecBAS - 100 octets

Utilisation de la convention ISU.

J'ai réalisé que le fait d'avoir une variable définie sur 1e3 (qui a besoin d'une instruction LET pour l'attribuer), puis d'utiliser cette variable dans l'élaboration, utilisait en fait plus de caractères que de coder en dur le 1e3 là où il était nécessaire.

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"

1

Rubis, 128 octets

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}

Je l'ai fait longtemps, c'est assez mauvais.

Production

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"

Éditer

TB ajouté pour 39 octets supplémentaires

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}

Production:

c[1000000000000] # => "1.0TB"

1

Sed -r, 218 + 1

J'utilise des unités SI; Je pense que choisir des unités binaires serait une politique courageuse . ;-)

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

Reformaté:

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

Production

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

Variations

Les règles semblent impliquer l'arrondi au plus proche, mais pour l'affichage humain, je pense que l'arrondi est une alternative acceptable et économise 123 octets (mieux que 50%):

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

L'extension naturelle à de plus grandes unités (toujours arrondie à la baisse, 130 + 1 octets):

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/

Sortie de variation:

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

Bon travail! J'aime que vous pensiez à toutes les différentes options!
Rolf

1

C, 77 75

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

Celui-ci utilise des unités SI et prend l'option 1000,0 ko pour l'arrondi.

Code développé:

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

Production

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

Pour obtenir des unités binaires, passez 1000à 1024et ajoutez ià la chaîne de format s'il y a un multiplicateur. Pour éviter l'arrondi à 4 chiffres, comparez >=.95au lieu de >=1. Pour accepter des unités plus grandes, étendez la uchaîne. En combinant toutes ces options, nous obtenons:

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

Sortie 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

Programme de test

Passez un nombre quelconque d'entrées comme arguments de ligne de commande:

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

int main(int argc, char **argv)
{
    while (*++argv) {
        printf("%s => ", *argv);
        f(strtod(*argv, 0));
        puts("");
    }
    return 0;
}

Nice one;) Bien exécuté!
Rolf

0

Rubis, 91 octets

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

Je pourrais probablement faire un peu mieux si j'essayais plus, mais voici ce que j'ai jusqu'à présent.


Utilisez 1024.au lieu de 1024.0.
mbomb007

0

Javascript ES5, 69 octets

Cela utilise une manière différente pour atteindre l'objectif final que @ edc65 a répondu .
En fait, c'est assez proche de ma réponse PHP .

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

Exécutez simplement l'extrait de pile ou collez-le sur votre console.


0

Rubis, 90 octets

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}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.