Formatieren Sie die angegebene Anzahl von Bytes in ein für Menschen lesbares Format

16

Herausforderung und Herkunft

Bei Stack Overflow lautet eine häufig gestellte Frage: Wie konvertiert man Bytes in ein für Menschen lesbares Format in Java? Die am häufigsten gewählte Antwort hat eine ganz gute Methode, um dies zu tun, aber das ist Codegolf und wir können es besser machen, nicht wahr?

Ihre Herausforderung besteht darin, eine Methode oder ein Programm zu schreiben, die bzw. das die angegebene Anzahl von Bytes in das korrekte, für Menschen lesbare Format konvertiert und das Ergebnis gemäß dem Standard in Ihrer Sprache ausgibt. *

* Siehe die Regeln für mehr Klarheit!

Eingang

Die Eingabe ist immer eine positive Anzahl von Bytes mit einem Maximum von (2 ^ 31) -1.

Ausgabe

Sie können wählen, ob Sie das Internationale Einheitensystem oder die Binärnotation als Ausgabe bevorzugen (die SI-Notation spart Ihnen wahrscheinlich einige Bytes).

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

Hinweis: Höhere Einheiten als GB oder GiB sind aufgrund des eingeschränkten Eingangsbereichs nicht möglich.

Beispielausgabe

Internationales Einheitensystem:

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är:

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

Regeln

  • Eingebaute Funktionen zur Byte-Formatierung sind nicht erlaubt!
  • Die Ausgabe sollte immer im selben Notationsstandard sein, Sie dürfen SI oder Binär nicht mischen;
  • Die Ausgabe sollte immer in der größtmöglichen Einheit erfolgen, wobei die resultierende Zahl immer noch höher oder gleich eins ist.
  • Die Ausgabe sollte immer eine Dezimalzahl haben. Sie können jedoch auch eine Ganzzahl ausgeben, wenn die resultierende Ausgabe in Byte (B) vorliegt.
  • Sie können wählen, ob Sie ein Leerzeichen, einen Tabulator oder nichts zwischen der Nummer und der Einheit einfügen möchten.
  • Die Eingabe erfolgt über STDIN oder Funktionsparameter;
  • Die Ausgabe wird auf die Konsole gedruckt oder als Zeichenfolge (oder ähnlicher Zeichencontainer) zurückgegeben.
  • Das ist Codegolf, also gewinnt die kürzeste Antwort. Habe Spaß!

Edit: Noch mehr Klarheit

Einige Zahlen haben ein interessantes Rundungsverhalten wie die Zahl 999950. Die meisten Codeimplementierungen würden 1000,0 kB anstelle von 1,0 MB zurückgeben. Warum? Denn 999950/1000 ergibt 999.950, was bei Verwendung von String.format in Java (auch in den meisten anderen Sprachen) effektiv auf 1000.0 gerundet wird. Um solche Fälle zu bearbeiten, sind einige zusätzliche Überprüfungen erforderlich.

Für diese Herausforderung werden beide Stile mit 1000,0 kB und 1,0 MB akzeptiert, obwohl der letzte Stil bevorzugt wird.

Pseudocode / Java-Testcode:


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 ツ
quelle
1
Technisch sollten SI-Kilobyte verwendet werden kB(beachten Sie den Kleinbuchstaben k)
SuperJedi224
Guter Punkt, behoben!
Rolf ツ
1
Ich möchte nicht auf viel beschränken, daher würde ich sagen, dass der Abstand inkonsistent sein kann. Aber mit dieser Regel: Der Unterschied in Raum und Tab - Zeichen für verschiedene gültige Eingaben können nicht überschreiten 10. (Um es alles ein bisschen „human readable“)
Rolfツ
2
Was ist die erwartete Ausgabe für 999999und 1000000? 160581Ausstellungsrundung, so soll es sein 1000.0kBund 1.0MB?
Sp3000,
3
@ Sp3000 Das ist eine gute Frage, die schönste Lösung wäre für 999999 1.0 MB anzuzeigen. Aber für diese Herausforderung würde ich sagen, dass 1000.0 KB und ähnliche Rundungsfälle auch in Ordnung sind.
Rolf ツ

Antworten:

10

TI-BASIC, 44

Wäre das richtige Werkzeug für den Job, wenn TI-BASIC eine halbwegs vernünftige String-Manipulation hätte (ich musste den Exponenten der Zahl, der in technischer Notation angezeigt wird, mit der Einheit überschreiben). Wie es ist, rundet es und gibt es korrekt aus, aber es ist noch nicht einmal nah an dem Gewinn von Einträgen. Vielleicht könnte eine andere Rechnersprache diese gewinnen?

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

Eingabe in das Formular [number]:[program name]auf dem Homescreen.

Gegebene Testfälle:

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
quelle
Ich hatte absolut keine Ahnung, dass TI-BASIC so vielseitig ist, haha
Beta Decay
1
TI-BASIC ist nicht vielseitig einsetzbar, aber für einige seiner Mängel gibt es oft seltsame Problemumgehungen.
Lirtosiast
6

CJam, 35 27 Bytes

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

Danke Dennis für das Entfernen von 8 Bytes.

Dies wird .0in der nicht gedruckt Online-Interpreter . Aber wie Dennis betont hat , funktioniert es im Java-Interpreter einwandfrei.

Erläuterung

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
quelle
ri{_e-3XmO_i}g;o]," kMG"='B(27 Bytes)
Dennis
@ Tennis Danke für die 1mO. Aber dieser Code funktioniert nicht für 1149999...
Jimmy23013
ri{_e-3_i}g;1mOo]," kMG"='Bsollte.
Dennis
Scratch das, das hat andere Fehler.
Dennis
999999wird 1000kB. Wenn ich die Frage noch einmal lese, bin ich mir nicht sicher, ob 1000kBes wirklich falsch wäre.
Dennis
5

Pyth, 29 27 Bytes

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

Demonstration. Testgeschirr.

Erläuterung:

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

CJam, 28

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

Probieren Sie es online aus

Hinweis: Beim Online-Interpreter wird ".0" nicht angezeigt, beim offiziellen Java-Interpreter jedoch .

Erläuterung:

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
quelle
Wofür ist das Backtick?
Dennis
@Dennis zeigt .0 im Online-Dolmetscher
aditsu
Im Java-Interpreter funktioniert es ohne das Backtick, also glaube ich nicht, dass Sie es brauchen.
Dennis
3

Python 2 - 76 Bytes

Verwendet das Internationale Einheitensystem, einfach weil es einfacher ist, es im Kopf zu tun;)

n=input();m=0;f=1e3
while n>=f:n/=f;m+=2
print"%.1f%s"%(n,'B kBMBGB'[m:m+2])
Beta-Zerfall
quelle
es scheint mir nicht in Ordnung zu sein, es respektiert nicht die angeforderte Formatierung, zum Beispiel wenn ich "2147483647" einreiche, erhalte ich "2.000000GB" - Die Frage verlangt eine Dezimalstelle und möglicherweise ein Leerzeichen.
Dieter
1
Auch dies ist 79 Bytes nach dieser . Das sind 75 Bytes. Ich glaube nicht, dass angegeben wurde, dass zwischen der Nummer und der Einheit ein Leerzeichen sein muss.
Kade,
Sie können ein Byte mit speichernf=1e3
mbomb007
@ mbomb007 Eigentlich hat es 2 Bytes gespart, weil 1e3 ein Float ist
Beta Decay
Ich wusste, dass es ein Schwimmer war. Ich glaube, ich kann einfach nicht zählen ...
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}

Verwendung

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

Haskell, 119

Leider konnte ich in Haskell keinen kürzeren Weg finden, um eine Dezimalstelle in Floats zu erhalten, aber ich poste für die Nachwelt.

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

Verwendung:

> m 160581
"160.6kB"

Mäßig weniger Golf Version:

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

Java, 106 Bytes

Dies ist eine Methode, die eine Zahl nimmt und eine Zeichenfolge zurückgibt.

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
quelle
Sie dürfen eine Funktion programmieren, die eine Zeichenfolge anstelle eines vollständigen Programms
Rolf olf
Drei Dinge: Wenn Sie ein ohnehin in ein doppeltes konvertieren (ich weiß nicht, ob es notwendig ist), können Sie 1e3für verwenden 1000; Sie können das while()in ein konvertieren for()und die freien Semikolons verwenden; und ich weiß nicht, ob dies funktioniert, weil es scheint, alle Dezimalstellen anzuzeigen, nicht nur eine nach der Dezimalstelle.
Lirtosiast
@ThomasKwa: Das habe ich zuletzt überprüft, die Frage schien das nicht explizit anzugeben. Aber ich denke, das tut es jetzt.
SuperJedi224,
1

Python 2, 127 Bytes

Verwenden der ISU. Das Snippet deklariert eine Funktion 'C', die die zu konvertierende Zahl als Argument verwendet.

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'

Ein Testcode:

    print 'Input\tOutput'
for v in [0,999,1000,1023,1023,1601,160581,4066888,634000000,2147483647]:
 print v,C(v)
Dieter
quelle
Sie können 1e3anstelle von1000.0
mbomb007
1

JavaScript ( ES6 ), 71

SI-Einheiten verwenden - Eine Funktion, die die angeforderte Zeichenfolge zurückgibt.

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

Diese kürzere folgt den Regeln, insbesondere 3 und 4

  • Die Ausgabe sollte immer in der größtmöglichen Einheit erfolgen, wobei die resultierende Zahl immer noch höher oder gleich eins ist als 995 => 1,0 kB
  • Die Ausgabe sollte immer eine Dezimalzahl haben. Sie können jedoch auch eine Ganzzahl ausgeben, wenn die resultierende Ausgabe in Byte (B) vorliegt. Ich wähle nicht, also 10 => 10.0 B

Auf diese Weise stimmen die Ergebnisse leider nicht mit den Beispielen überein.

Um den Beispielen zu entsprechen, ist hier ein längeres, spezielles Gehäuse für kleine Zahlen (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)

Führen Sie das zu testende Snippet aus (nur in EcmaScript 6, Firefox)

edc65
quelle
1

Python, 61 Bytes

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

Rufen Sie gerne an f(999). Beachten Sie, dass dies 1e3ein Float ist, sodass dies sowohl mit Python 2 als auch mit Python 3 funktioniert.

Sp3000
quelle
1

PHP4.1, 63 62 Bytes

Nicht das beste Golfspiel, aber sicherlich recht kurz.

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

Um es zu benutzen, greifen Sie über POST / GET zu oder setzen Sie einen Wert in der SESSION auf den Schlüssel B.

Lass den Schlüssel frei I!

Ismael Miguel
quelle
1

SpecBAS - 100 Bytes

Verwendung der ISU-Konvention.

Ich erkannte, dass eine Variable, die auf 1e3 gesetzt war (für deren Zuweisung eine LET-Anweisung erforderlich ist), mehr Zeichen verwendete, als nur die 1e3 dort zu codieren, wo sie benötigt wurde.

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

Ich habe es lange gemacht, das ist ziemlich schlimm.

Ausgabe

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"

Bearbeiten

TB für zusätzliche 39 Bytes hinzugefügt

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}

Ausgabe:

c[1000000000000] # => "1.0TB"
Bloße Kraft
quelle
1

Sed -r, 218 + 1

Ich verwende SI-Einheiten. Ich denke, dass die Wahl von binären Einheiten eine mutige Politik wäre . ;-)

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

Neuformatiert:

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

Ausgabe

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

Variationen

Die Regeln scheinen das Runden auf die nächste Stelle zu implizieren, aber für die menschliche Darstellung halte ich das Runden für eine akzeptable Alternative und spart 123 Bytes (besser als 50%):

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

Die natürliche Erweiterung auf größere Einheiten (noch aufgerundet, 130 + 1 Byte):

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/

Variationsausgabe:

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
quelle
Gut gemacht! Ich finde es gut, dass du über all die verschiedenen Optionen nachgedacht hast!
Rolf ツ
1

C 77, 75

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

Dies verwendet SI-Einheiten und verwendet die 1000.0kB-Option zum Runden.

Erweiterter Code:

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

Ausgabe

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

Varianten

Um binäre Einheiten zu erhalten, ändern Sie diese 1000in 1024und ergänzen Sie idie Formatzeichenfolge, wenn ein Multiplikator vorhanden ist. Vergleichen Sie >=.95statt , um eine Rundung mit 4 Stellen zu vermeiden >=1. Wenn Sie größere Einheiten akzeptieren möchten, verlängern Sie die uZeichenfolge. Durch die Kombination all dieser Optionen erhalten wir:

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

Variantenausgabe

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

Testprogramm

Übergeben Sie eine beliebige Anzahl von Eingaben als Befehlszeilenargumente:

#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
quelle
Schön;) Gut ausgeführt!
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]}"

Ich könnte es wahrscheinlich ein bisschen besser machen, wenn ich mich mehr anstrengen würde, aber hier ist, was ich bisher habe.

David Bailey
quelle
Verwenden Sie 1024.anstelle von 1024.0.
mbomb007
0

Javascript ES5, 69 Bytes

Dies verwendet einen anderen Weg, um das Endziel der Antwort von @ edc65 zu erreichen .
Eigentlich ist das ziemlich nah an meiner PHP-Antwort .

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

Führen Sie einfach das Stack-Snippet aus oder fügen Sie es in Ihre Konsole ein.

Ismael Miguel
quelle
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
quelle