Định dạng số byte đã cho thành định dạng có thể đọc được


16

Thử thách và nguồn gốc

Trên Stack Overflow một câu hỏi phổ biến là: Làm thế nào để chuyển đổi kích thước byte thành định dạng có thể đọc được của con người trong java? Câu trả lời được bình chọn nhiều nhất có một phương pháp khá hay để làm điều này, nhưng đây là codegolf và chúng ta có thể làm tốt hơn, phải không?

Thách thức của bạn là viết một phương thức hoặc chương trình bao phủ số byte đã cho thành định dạng có thể đọc được của con người và in kết quả ra chuẩn theo ngôn ngữ của bạn. *

* Xem các quy tắc để làm rõ hơn!

Đầu vào

Đầu vào sẽ luôn là số byte dương với tối đa là (2 ^ 31) -1.

Đầu ra

Bạn có thể chọn nếu bạn thích Hệ thống đơn vị quốc tế hoặc ký hiệu nhị phân làm đầu ra (ký hiệu SI có thể giúp bạn tiết kiệm một số byte).

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

Lưu ý: Các đơn vị cao hơn GB hoặc GiB không thể nhìn thấy do phạm vi đầu vào bị hạn chế.

Ví dụ đầu ra

Hệ thống đơn vị quốc tế:

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

Nhị phân:

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

Quy tắc

  • Các hàm dựng sẵn để định dạng byte không được phép!
  • Đầu ra phải luôn ở cùng một tiêu chuẩn ký hiệu, bạn không được trộn lẫn SI hoặc nhị phân;
  • Đầu ra phải luôn ở đơn vị lớn nhất có thể trong đó số kết quả vẫn cao hơn hoặc bằng một;
  • Đầu ra phải luôn có một số thập phân, nhưng bạn có thể chọn in một số nguyên khi đầu ra kết quả tính bằng byte (B);
  • Bạn có thể chọn nếu bạn muốn thêm khoảng trắng, tab hoặc không có gì giữa số và đơn vị;
  • Đầu vào được nhận thông qua STDIN hoặc các tham số chức năng;
  • Đầu ra được in ra bàn điều khiển hoặc được trả về dưới dạng chuỗi (hoặc thùng chứa ký tự tương tự);
  • Đây là mã golf, vì vậy câu trả lời ngắn nhất sẽ thắng. Chúc vui vẻ!

Chỉnh sửa: Làm rõ hơn nữa

Một số số có hành vi làm tròn thú vị như số 999950. Hầu hết các triển khai mã sẽ trả về 1000.0 kB thay vì 1.0 MB. Tại sao? Bởi vì 999950/1000 ước tính thành 999.950, được làm tròn một cách hiệu quả thành 1000.0 khi sử dụng String.format trong Java (trong hầu hết các ngôn ngữ khác). Hench một số kiểm tra thêm là cần thiết để xử lý các trường hợp như thế này.

Đối với thử thách này cả hai kiểu, 1000.0 kB và 1.0 MB được chấp nhận, mặc dù kiểu cuối cùng được ưa thích.

Mã giả / mã kiểm tra 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
Về mặt kỹ thuật, nên sử dụng kilobyte SI kB(lưu ý chữ thường k)
SuperJedi224

Điểm tốt, cố định!
Rolf

1
Tôi không muốn giới hạn nhiều, vì vậy tôi sẽ nói khoảng cách có thể không nhất quán. Nhưng với quy tắc này: Sự khác biệt về ký tự không gian và tab cho các đầu vào hợp lệ khác nhau không được vượt quá 10. (Để giữ cho tất cả một chút "con người có thể đọc được")
Rolf

2
Sản lượng dự kiến ​​cho 999999và là 1000000gì? 160581Triển lãm làm tròn, vậy nó nên 1000.0kB1.0MB?
Sp3000

3
@ Sp3000 Đó là một câu hỏi hay, giải pháp tốt nhất sẽ là 999999 để hiển thị 1.0 MB. Nhưng đối với thử thách này, tôi sẽ nói 1000.0 KB và các trường hợp làm tròn tương tự cũng tốt.
Rolf

Câu trả lời:


10

TI-BASIC, 44

Sẽ là công cụ phù hợp cho công việc nếu TI-BASIC có thao tác chuỗi nửa chừng (tôi phải dùng đến cách ghi đè số mũ của số, được hiển thị trong ký hiệu kỹ thuật, với đơn vị). Vì nó làm tròn và xuất ra chính xác, nhưng nó thậm chí không gần với các mục chiến thắng. Có lẽ một ngôn ngữ máy tính khác có thể giành chiến thắng này?

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

Nhập vào mẫu [number]:[program name]trên màn hình chính.

Cho các trường hợp thử nghiệm:

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

Tôi hoàn toàn không biết rằng TI-BASIC rất linh hoạt haha
Beta Decay

1
TI-BASIC không linh hoạt, nhưng thường có những cách giải quyết lạ cho một vài khuyết điểm của nó.
lirtosiast

6

CJam, 35 27 byte

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

Cảm ơn Dennis vì đã xóa 8 byte.

Điều này không in .0trong trình thông dịch trực tuyến . Nhưng như Dennis đã chỉ ra , nó hoạt động tốt trong trình thông dịch Java.

Giải trình

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 byte)
Dennis

@Dennis Cảm ơn vì 1mO. Nhưng mã này không hoạt động cho 1149999...
jimmy23013

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

Cào đó, có lỗi khác.
Dennis

999999trở thành 1000kB. Đọc lại câu hỏi, tôi không chắc liệu 1000kBthực sự có sai không.
Dennis

5

Bình thường, 29 27 byte

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

Trình diễn. Khai thác thử nghiệm.

Giải trình:

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

Dùng thử trực tuyến

Lưu ý: nó không hiển thị ".0" với trình thông dịch trực tuyến, nhưng làm như vậy với trình thông dịch java chính thức .

Giải trình:

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'

Backtick để làm gì?
Dennis

@Dennis hiển thị .0 trong trình thông dịch trực tuyến
aditsu

Nó hoạt động tốt trong trình thông dịch Java mà không cần backtick, vì vậy tôi không nghĩ bạn cần nó.
Dennis

3

Python 2 - 76 byte

Sử dụng Hệ thống đơn vị quốc tế, đơn giản vì nó dễ thực hiện hơn trong đầu bạn;)

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

đối với tôi có vẻ không ổn, nó không tôn trọng định dạng được hỏi, ví dụ nếu tôi gửi "2147483647" Tôi nhận được "2.000000GB" - Câu hỏi yêu cầu một số thập phân và có thể là khoảng trắng.
ăn kiêng

1
Ngoài ra, đây là 79 byte theo điều này . Đây là 75 byte. Tôi không tin rằng nó đã được chỉ định rằng cần phải có một khoảng cách giữa số và đơn vị.
Kade

bạn có thể lưu một byte vớif=1e3
mbomb007

@ mbomb007 Trên thực tế, nó đã lưu 2 byte vì 1e3 là số float
Beta Decay

Tôi biết đó là một cái phao. Tôi đoán là tôi không thể đếm được ...
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}

sử dụng

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

Đáng buồn là tôi không tìm thấy một cách ngắn hơn trong Haskell để đảm bảo 1 vị trí thập phân trong số float, nhưng tôi đang đăng cho hậu thế.

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

Sử dụng:

> m 160581
"160.6kB"

Phiên bản ít chơi gôn hơn:

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 byte

Đây là một phương thức lấy một số và trả về một chuỗi.

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

Bạn được phép lập trình một hàm trả về một chuỗi thay vì một chương trình hoàn chỉnh, nó có thể giúp bạn tiết kiệm một số byte;)
Rolf

Ba điều: Nếu bạn đang chuyển đổi một gấp đôi (tôi không biết nếu cần thiết), bạn có thể sử dụng 1e3cho 1000; bạn có thể chuyển đổi nó while()thành a for()và sử dụng dấu chấm phẩy miễn phí; và tôi không biết nếu nó hoạt động bởi vì nó dường như hiển thị tất cả các chữ số thập phân, không chỉ một chữ số ở vị trí thập phân.
lirtosiast

@ThomasKwa: Lần cuối tôi kiểm tra, câu hỏi dường như không xác định rõ ràng điều đó. Nhưng tôi đoán nó làm bây giờ.
SuperJedi224

1

Python 2, 127 byte

Sử dụng ISU. Đoạn mã khai báo một hàm 'C' lấy số được chuyển đổi làm đối số.

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'

Một số mã kiểm tra:

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

Bạn có thể sử dụng 1e3thay vì1000.0
mbomb007

1

JavaScript ( ES6 ), 71

Sử dụng các đơn vị SI - Một hàm trả về chuỗi được yêu cầu.

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

Cái này ngắn hơn tuân theo các quy tắc, đặc biệt là 3 và 4

  • Đầu ra phải luôn ở đơn vị lớn nhất có thể trong đó số kết quả vẫn cao hơn hoặc bằng một thì 995 => 1.0kB
  • Đầu ra phải luôn có một số thập phân, nhưng bạn có thể chọn in một số nguyên khi đầu ra kết quả tính bằng byte (B) Tôi chọn không, vì vậy 10 => 10.0 B

Than ôi, theo cách này, kết quả không phù hợp với các ví dụ.

Để khớp với các ví dụ, đây là một ví dụ dài hơn, đặc biệt dành cho số nhỏ (82 byte)

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

Chạy đoạn mã để kiểm tra (chỉ là EcmaScript 6, chỉ dành cho Firefox)


1

Python, 61 byte

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

Gọi như thế f(999). Lưu ý rằng đó 1e3là một float, vì vậy điều này hoạt động với cả Python 2 và Python 3.


1

PHP4.1, 63 62 byte

Không phải là golf tốt nhất, nhưng chắc chắn là khá ngắn.

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

Để sử dụng nó, truy cập qua POST / GET hoặc đặt một giá trị trong SESSION, trên phím B.

Để lại chìa khóa chưa được Iđặt!


1

SpecBAS - 100 byte

Sử dụng quy ước ISU.

Tôi nhận ra rằng có một biến được đặt thành 1e3 (cần một câu lệnh LET để gán nó), và sau đó sử dụng biến đó trong quá trình làm việc, thực sự đã sử dụng nhiều ký tự hơn là chỉ mã hóa 1e3 khi cần.

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

Ruby, 128 byte

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}

Tôi đã làm điều đó một cách lâu dài, điều này là khá xấu.

Đầu ra

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"

Biên tập

Đã thêm TB cho thêm 39 byte

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}

Đầu ra:

c[1000000000000] # => "1.0TB"

1

Trầm tích -r, 218 + 1

Tôi đang sử dụng các đơn vị SI; Tôi nghĩ rằng việc lựa chọn các đơn vị nhị phân sẽ là một chính sách can đảm . ;-)

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

Định dạng lại:

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

Đầu ra

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

Biến thể

Các quy tắc dường như ngụ ý từ tròn đến gần nhất, nhưng đối với màn hình của con người, tôi tin rằng làm tròn xuống là một sự thay thế chấp nhận được và tiết kiệm 123 byte (tốt hơn 50%):

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

Phần mở rộng tự nhiên cho các đơn vị lớn hơn (vẫn làm tròn xuống, 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/

Đầu ra biến thể:

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

Bạn đã làm rất tốt! Tôi thích rằng bạn nghĩ về tất cả các tùy chọn khác nhau!
Rolf

1

C, 77 75

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

Điều này sử dụng các đơn vị SI và lấy tùy chọn 1000.0kB để làm tròn.

Mã mở rộng:

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

Đầu ra

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

Biến thể

Để có được các đơn vị nhị phân, thay đổi 1000đến 1024, và thêm ivào chuỗi định dạng nếu có một số nhân. Để tránh làm tròn 4 chữ số, hãy so sánh >=.95thay vì >=1. Để chấp nhận các đơn vị lớn hơn, mở rộng uchuỗi. Kết hợp tất cả các tùy chọn này, chúng tôi nhận được:

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

Biến đầu ra

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

Chương trình kiểm tra

Vượt qua bất kỳ số lượng đầu vào dưới dạng đối số dòng lệnh:

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

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

Đẹp một;) Thực hiện tốt!
Rolf

0

Ruby, 91 byte

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

Tôi có thể có thể làm tốt hơn một chút nếu tôi cố gắng hơn nhưng đây là những gì tôi đã có cho đến nay.


Sử dụng 1024.thay vì 1024.0.
mbomb007


0

Ruby, 90 byte

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}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.