Sformatuj podaną liczbę bajtów na format czytelny dla człowieka

16

Wyzwanie i pochodzenie

W przypadku przepełnienia stosu popularne pytanie brzmi: jak przekonwertować rozmiar bajtu na format czytelny dla ludzi w Javie? Najwyżej głosowana odpowiedź ma całkiem niezłą metodę na zrobienie tego, ale jest to codegolf i możemy to zrobić lepiej, prawda?

Wyzwanie polega na napisaniu metody lub programu, który pokryje podaną liczbę bajtów w poprawnym formacie czytelnym dla człowieka i wydrukuje wynik na standardowym z twojego języka. *

* Zobacz zasady, aby uzyskać więcej wyjaśnień!

Wejście

Dane wejściowe zawsze będą dodatnią liczbą bajtów z maksymalną wartością (2 ^ 31) -1.

Wynik

Możesz wybrać, czy wolisz Międzynarodowy System Jednostek, czy zapis binarny jako wynik (notacja SI prawdopodobnie oszczędza ci trochę bajtów).

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

Uwaga: Jednostki wyższe niż GB lub GiB nie są możliwe ze względu na ograniczony zakres wejściowy.

Przykładowe dane wyjściowe

Międzynarodowy system jednostek:

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

Dwójkowy:

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

Zasady

  • Wbudowane funkcje formatowania bajtów są niedozwolone!
  • Dane wyjściowe powinny zawsze mieć ten sam standard notacji, nie można mieszać SI ani binarnych;
  • Wyjście powinno zawsze być w największej możliwej jednostce, gdzie wynikowa liczba jest wciąż wyższa lub równa jeden;
  • Dane wyjściowe powinny zawsze mieć jedną liczbę dziesiętną, ale możesz wybrać wydrukowanie liczby całkowitej, jeśli wynik jest w bajtach (B);
  • Możesz wybrać, czy chcesz dodać spację, tabulator lub nic między liczbą a jednostką;
  • Wejście jest odbierane przez STDIN lub parametry funkcji;
  • Dane wyjściowe są drukowane na konsoli lub zwracane jako ciąg znaków (lub podobny kontener znaków);
  • To jest golf golfowy, więc wygrywa najkrótsza odpowiedź. Baw się dobrze!

Edycja: jeszcze więcej wyjaśnień

Niektóre liczby mają ciekawe zachowania zaokrąglania, takie jak liczba 999950. Większość implementacji kodu zwróciłoby 1000,0 kB zamiast 1,0 MB. Dlaczego? Ponieważ 999950/1000 ocenia na 999,950, który jest efektywnie zaokrąglany do 1000,0 przy użyciu String.format w Javie (także w większości innych języków). Konieczne są dodatkowe kontrole do obsługi takich przypadków.

W przypadku tego wyzwania akceptowane są oba style, 1000,0 kB i 1,0 MB, chociaż preferowany jest ostatni styl.

Pseudo kod / kod testowy 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 ツ
źródło
1
Technicznie rzecz biorąc, należy użyć kilobajtów SI kB(zwróć uwagę na małą literę k)
SuperJedi224,
Dobry punkt, naprawiony!
Rolf ツ
1
Nie chcę ograniczać się zbytnio, więc powiedziałbym, że odstępy mogą być niespójne. Ale z tą zasadą: Różnica w znakach spacji i tabulacji dla różnych prawidłowych danych wejściowych nie może przekraczać 10. (Aby wszystko było trochę „czytelne dla człowieka”)
Rolf ツ
2
Jaka jest oczekiwana wydajność dla 999999i 1000000? 160581wykazuje zaokrąglenie, więc powinno być 1000.0kBi 1.0MB?
Sp3000,
3
@ Sp3000 To dobre pytanie, najlepszym rozwiązaniem byłoby wyświetlenie 999999 1,0 MB. Ale w przypadku tego wyzwania powiedziałbym, że 1000,0 KB i podobne przypadki zaokrąglania również są w porządku.
Rolf ツ

Odpowiedzi:

10

TI-BASIC, 44

Byłoby właściwym narzędziem do pracy, gdyby TI-BASIC miał w połowie przyzwoitą manipulację ciągiem (musiałem uciekać się do zastąpienia wykładnika liczby, wyświetlanej w notacji inżynierskiej, jednostką). Ponieważ tak jest, zaokrągla i generuje poprawnie, ale nie jest nawet bliski zwycięskich zgłoszeń. Może inny język kalkulatora mógłby wygrać?

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

Wprowadź formularz [number]:[program name]na ekranie głównym.

Podane przypadki testowe:

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
źródło
Absolutnie nie miałem pojęcia, że ​​TI-BASIC jest tak wszechstronny haha
Beta Decay
1
TI-BASIC nie jest wszechstronny, ale często występują dziwne obejścia kilku jego wad.
lirtosiast
6

CJam, 35 27 bajtów

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

Dzięki Dennis za usunięcie 8 bajtów.

To nie jest drukowane .0w tłumaczu online . Ale jak zauważył Dennis , działa dobrze w interpreter Java.

Wyjaśnienie

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
źródło
ri{_e-3XmO_i}g;o]," kMG"='B(27 bajtów)
Dennis
@Dennis Dzięki za 1mO. Ale ten kod nie działa dla 1149999...
jimmy23013
ri{_e-3_i}g;1mOo]," kMG"='Bpowinien.
Dennis
Zdrap to, że ma inne błędy.
Dennis
999999staje się 1000kB. Czytając to pytanie jeszcze raz, nie jestem pewien, czy 1000kBrzeczywiście byłoby w błędzie.
Dennis,
5

Pyth, 29 27 bajtów

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

Demonstracja. Uprząż testowa.

Wyjaśnienie:

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
źródło
3

CJam, 28

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

Wypróbuj online

Uwaga: nie wyświetla on „.0” z tłumaczem online, ale robi to z oficjalnym tłumaczem Java .

Wyjaśnienie:

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
źródło
Do czego służy backtick?
Dennis,
@Dennis pokazuje .0 w internetowym
tłumaczu
Działa dobrze w interpreter Java bez backtick, więc nie sądzę, żebyś go potrzebował.
Dennis,
3

Python 2 - 76 bajtów

Korzysta z międzynarodowego systemu jednostek, po prostu dlatego, że łatwiej to zrobić w twojej głowie;)

n=input();m=0;f=1e3
while n>=f:n/=f;m+=2
print"%.1f%s"%(n,'B kBMBGB'[m:m+2])
Rozpad beta
źródło
wydaje mi się, że nie jest ok, nie szanuje zadanego formatowania, na przykład, jeśli prześlę „2147483647” Otrzymam „2.000000GB” - Pytanie wymaga jednej dziesiętnej, a może spacji.
dieter
1
Ponadto jest to 79 bajtów zgodnie z tym . To jest 75 bajtów. Nie sądzę, aby zostało określone, że między liczbą a jednostką musi być spacja.
Kade,
możesz zapisać jeden bajt za pomocąf=1e3
mbomb007
@ mbomb007 Właściwie to zaoszczędziło 2 bajty, ponieważ 1e3 jest liczbą zmiennoprzecinkową
Beta Decay
Wiedziałem, że to pływak. Chyba po prostu nie mogę liczyć ...
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}

stosowanie

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
źródło
2

Haskell, 119

Niestety nie mogę znaleźć krótszej drogi w Haskell, aby zapewnić 1 miejsce po przecinku w liczbach zmiennoprzecinkowych, ale wysyłam wiadomość o potomności.

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

Stosowanie:

> m 160581
"160.6kB"

Wersja średnio mniej golfowa:

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
źródło
2

Java, 106 bajtów

To metoda, która pobiera liczbę i zwraca ciąg znaków.

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
źródło
Możesz zaprogramować funkcję zwracającą ciąg zamiast pełnego programu, może to zaoszczędzić trochę bajtów;)
Rolf ツ
Trzy rzeczy: jeśli i tak konwertujesz na podwójne (nie wiem, czy to konieczne), możesz użyć 1e3dla 1000; możesz przekonwertować to while()na a for()i użyć wolnych średników; i nie wiem, czy to działa, ponieważ wydaje się, że wyświetla wszystkie cyfry dziesiętne, a nie tylko jedną po przecinku.
lirtosiast
@ThomasKwa: Ostatnio sprawdziłem, wydaje się, że pytanie wyraźnie tego nie określało. Ale chyba tak jest teraz.
SuperJedi224,
1

Python 2, 127 bajtów

Korzystanie z ISU. Fragment deklaruje funkcję „C”, która przyjmuje liczbę do przekonwertowania jako 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'

Niektóre kody testowe:

    print 'Input\tOutput'
for v in [0,999,1000,1023,1023,1601,160581,4066888,634000000,2147483647]:
 print v,C(v)
dieter
źródło
Możesz użyć 1e3zamiast1000.0
mbomb007
1

JavaScript ( ES6 ), 71

Korzystanie z jednostek SI - funkcja zwracająca żądany ciąg.

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

Ten krótszy jest zgodny z zasadami, szczególnie 3 i 4

  • Wyjście powinno zawsze być w największej możliwej jednostce, gdzie wynikowa liczba jest wciąż wyższa lub równa jeden, a następnie 995 => 1,0 kB
  • Dane wyjściowe powinny zawsze mieć jedną liczbę dziesiętną, ale możesz wydrukować liczbę całkowitą, gdy wynik jest w bajtach (B) Nie wybieram, więc 10 => 10,0 B

Niestety, w ten sposób wyniki nie pasują do przykładów.

Aby pasować do przykładów, tutaj jest dłuższy, specjalnie zaprojektowany dla małych liczb (82 bajtów)

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

Uruchom fragment kodu, aby przetestować (tylko EcmaScript 6, tylko Firefox)

edc65
źródło
1

Python, 61 bajtów

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

Zadzwoń jak f(999). Zauważ, że 1e3jest to liczba zmiennoprzecinkowa, więc działa to zarówno z Python 2, jak i Python 3.

Sp3000
źródło
1

PHP4.1, 63 62 bajty

Nie najlepsza gra w golfa, ale na pewno jest dość krótka.

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

Aby go użyć, uzyskaj dostęp przez POST / GET lub ustaw wartość w SESSION na klawiszu B.

Pozostaw klucz Inieuzbrojony!

Ismael Miguel
źródło
1

SpecBAS - 100 bajtów

Korzystanie z konwencji ISU.

Uświadomiłem sobie, że mając zmienną ustawioną na 1e3 (która wymaga przypisania instrukcji LET), a następnie używając tej zmiennej przy opracowywaniu, faktycznie użyłem więcej znaków niż po prostu zakodowałem 1e3 tam, gdzie było to potrzebne.

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
źródło
1

Rubin, 128 bajtów

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}

Zrobiłem to na dłuższą metę, jest całkiem źle.

Wynik

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"

Edytować

Dodano TB dla dodatkowych 39 bajtów

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}

Wynik:

c[1000000000000] # => "1.0TB"
Sheerforce
źródło
1

Sed -r, 218 + 1

Używam jednostek SI; Myślę, że wybór jednostek binarnych byłby odważną polityką . ;-)

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

Przeformatowany:

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

Wynik

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

Wariacje

Reguły wydają się sugerować zaokrąglanie do najbliższego, ale moim zdaniem zaokrąglanie w dół jest akceptowalną alternatywą i pozwala zaoszczędzić 123 bajty (więcej niż 50%):

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

Naturalne rozszerzenie do większych jednostek (wciąż zaokrąglające w dół, 130 + 1 bajtów):

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/

Wariacja wyjściowa:

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
źródło
Dobra robota! Podoba mi się, że pomyślałeś o wszystkich różnych opcjach!
Rolf ツ
1

C, 77 75

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

Wykorzystuje jednostki SI i przyjmuje opcję 1000,0 kB do zaokrąglania.

Rozszerzony kod:

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

Wynik

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

Warianty

Aby uzyskać jednostki binarne, zmiany 1000do 1024i dodać ido tekstu formatu czy jest mnożnikiem. Aby uniknąć zaokrąglania 4 cyfr, porównaj >=.95zamiast >=1. Aby zaakceptować większe jednostki, przedłuż uciąg. Łącząc wszystkie te opcje, otrzymujemy:

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

Wariant wyjściowy

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

Program testowy

Przekaż dowolną liczbę danych wejściowych jako argumenty wiersza polecenia:

#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
źródło
Fajny;) Dobrze wykonany!
Rolf ツ
0

Ruby, 91 bajtów

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

Prawdopodobnie mógłbym zrobić trochę lepiej, gdybym spróbował mocniej, ale oto, co mam do tej pory.

David Bailey
źródło
Posługiwać się 1024. zamiast 1024.0.
mbomb007
0

JavaScript ES5, 69 bajtów

To używa innego sposobu, aby osiągnąć cel końcowy, którym jest @ edc65 .
W rzeczywistości jest to dość bliskie mojej odpowiedzi na PHP .

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

Po prostu uruchom fragment kodu lub wklej go na konsoli.

Ismael Miguel
źródło
0

Rubinowy, 90 bajtów

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
źródło