将给定数量的字节格式化为可读格式


16

挑战与起源

在Stack Overflow上,一个流行的问题是:如何在Java中将字节大小转换为人类可读的格式?投票最多的答案有一个很好的方法可以做到这一点,但这是代码高尔夫,我们可以做得更好,不是吗?

您面临的挑战是编写一种方法或程序,该方法或程序可以将给定字节数转换为正确的人类可读格式,并使用您的语言将结果打印为标准格式。*

*请参阅规则以进行进一步说明!

输入值

输入将始终为正数字节,最大(2 ^ 31)-1。

输出量

您可以选择是否使用国际单位制或二进制表示法作为输出(SI表示法可能为您节省一些字节)。

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

注意:由于输入范围的限制,无法使用高于GB或GiB的单位。

输出示例

国际单位制:

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

二进制:

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

规则

  • 不允许用于字节格式化的内置函数!
  • 输出应始终采用相同的注释标准,不得混用SI或二进制;
  • 输出应始终以可能的最大单位为单位,其中结果数仍大于或等于1;
  • 输出应始终有一个十进制数,但是当结果输出以字节(B)为单位时,您可以选择打印一个整数。
  • 您可以选择是否要在数字和单位之间添加空格,制表符或不添加任何内容。
  • 通过STDIN或功能参数接收输入;
  • 输出被打印到控制台或作为字符串(或类似的字符容器)返回;
  • 这是代码高尔夫球,因此最短的答案为胜。玩得开心!

编辑:更加澄清

一些数字具有有趣的舍入行为,例如数字999950。大多数代码实现将返回1000.0 kB而不是1.0 MB。为什么?因为999950/1000的计算结果为999.950,所以在Java中使用String.format时(实际上在大多数其他语言中),有效地舍入为1000.0。需要处理一些额外的检查来处理这样的情况。

对于此挑战,尽管首选最后一种样式,但都接受1000.0 kB和1.0 MB这两种样式。

伪代码/ 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
从技术上讲,应使用SI千字节kB(请注意小写k)
SuperJedi224

好点,固定!
罗夫·ツ

1
我不想限制太多,所以我会说间距可能不一致。但要遵循以下规则:不同有效输入的空格和制表符的差异不得超过10。(为了使它们有点“人类可读”)
Rolf

2
什么是预期产出9999991000000160581展示四舍五入,应该是1000.0kB1.0MB吗?
Sp3000

3
@ Sp3000这是一个好问题,最好的解决方案是让999999显示1.0 MB。但是对于这个挑战,我想说1000.0 KB和类似的舍入大小写也可以。
罗夫·ツ

Answers:


10

TI-BASIC,44岁

如果TI-BASIC具有中途的字符串操作(我不得不求助于用单位覆盖以工程符号显示的数字的指数),它将是完成这项工作的正确工具。因为它是四舍五入并正确输出,但它甚至与获胜作品还差得很远。也许另一种计算器语言可以胜出吗?

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

输入形式 [number]:[program name]主屏幕上的。

给定测试用例:

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

我完全不知道TI-BASIC是如此多用途哈哈
Beta Decay 2015年

1
TI-BASIC并不是通用的,但是对于它的一些缺点通常有一些奇怪的解决方法。
lirtosiast,2015年

6

CJam,35 27字节

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

感谢Dennis删除了8个字节。

这不会.0在线解释器中打印。但正如丹尼斯指出的那样,它在Java解释器中可以正常工作。

说明

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个字节)
丹尼斯

@Dennis谢谢1mO。但是此代码不适用于1149999...
jimmy23013 2015年

ri{_e-3_i}g;1mOo]," kMG"='B应该。
丹尼斯

从头开始,还有其他错误。
丹尼斯

999999成为1000kB。再次阅读问题,我不确定是否1000kB真的错了。
丹尼斯

5

Pyth,29 27字节

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

示范。 测试线束。

说明:

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

在线尝试

注意:在线解释器不会显示“ .0”,而官方的Java解释器会显示“ .0” 。

说明:

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'

反击是为了什么?
丹尼斯2015年

@Dennis在在线翻译中显示.0
aditsu

在没有反引号的Java解释器中,它可以正常工作,因此我认为您不需要它。
丹尼斯

3

Python 2-76个字节

使用国际单位制,仅仅是因为它在您的脑海中更容易做到;)

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

对我来说似乎不行,它不尊重所要求的格式,例如,如果我提交“ 2147483647”,我将获得“ 2.000000GB”。该问题要求输入一个小数,甚至一个空格。
节食者

1
另外,根据this,这是79个字节。是75个字节。我不认为指定数字和单位之间必须有空格。
卡德2015年

您可以使用f=1e3
mbomb007

@ mbomb007实际上节省了2个字节,因为1e3是浮点型
Beta Decay

我知道那是浮游物。我想我无法计数...
mbomb007

2

动力壳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}

用法

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

哈斯克尔(119)

可悲的是,我无法在Haskell中找到一种更短的方法来确保浮点数保持小数点后一位,但我是为了后代而发帖。

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

用法:

> m 160581
"160.6kB"

适度减少打高尔夫球的版本:

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个字节

这是一个采用数字并返回字符串的方法。

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

您可以编写返回字符串的函数,而不是编写完整的程序,这可以为您节省一些字节;)
Rolf 1515年

三件事情:如果你转换到双反正(我不知道,如果有必要),就可以使用1e31000; 您可以将其转换while()为a for()并使用免费的分号;而且我不知道这是否可行,因为它似乎显示所有十进制数字,而不仅仅是小数点后一位。
lirtosiast

@ThomasKwa:最后我检查了一下,这个问题似乎没有明确指出。但是我想现在可以了。
SuperJedi224

1

Python 2,127字节

使用ISU。该代码段声明了一个函数“ C”,该函数将要转换的数字作为参数。

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'

一些测试代码:

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

您可以使用1e3,而不是1000.0
mbomb007

1

JavaScript( ES6),71

使用SI单位-返回所请求字符串的函数。

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

这个较短的规则遵循规则,尤其是3和4

  • 输出应始终以可能的最大单位为单位,其中结果数仍大于或等于1 然后995 => 1.0kB
  • 输出应该始终有一个十进制数,但是当结果输出以字节为单位(B)时,您可以选择打印一个整数,而我选择不是,所以10 => 10.0 B

las,这样,结果与示例不匹配。

为了与示例匹配,这是一个较长的示例,对小数字(82字节)有特殊情况

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

运行代码片段进行测试(仅限EcmaScript 6,仅Firefox)


1

Python,61个字节

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

打电话喜欢f(999)。注意1e3是一个浮点数,因此适用于Python 2和Python 3。


1

PHP4.1, 63 62个字节

不是最好的高尔夫球场,但是肯定很短。

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

要使用它,请通过POST / GET访问或在SESSION中的键上设置一个值 B

保持键不动I


1

SpecBAS-100字节

使用ISU约定。

我意识到,将一个变量设置为1e3(需要使用LET语句来分配它),然后在计算中使用该变量,实际上所用的字符比仅对需要的1e3进行硬编码的字符更多。

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个字节

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}

我做了很长的路,这很糟糕。

输出量

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"

编辑

增加了TB,额外增加了39个字节

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}

输出:

c[1000000000000] # => "1.0TB"

1

塞德-r,218 + 1

我正在使用SI单位;我认为选择二进制单位将是一个勇敢的政策。;-)

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

重新格式化:

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

输出量

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

变化

该规则似乎意味着要四舍五入,但是对于人类的显示,我认为四舍五入是可以接受的替代方法,它可以节省123个字节(小于50%):

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

对较大单位的自然扩展(仍向下舍入,130 + 1字节):

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/

变化输出:

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

做得好!我喜欢您考虑了所有不同的选择!
罗尔夫(Rolf)2015年

1

C,77 75

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

这使用SI单位,并采用1000.0kB选项进行舍入。

扩展代码:

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

输出量

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

变体

要获取二进制单位,请更改10001024i如果有乘数,请添加到格式字符串。为避免四位数舍入,请比较>=.95而不是>=1。要接受更大的单位,请扩展u字符串。结合所有这些选项,我们得到:

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

变体输出

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

测试程序

传递任意数量的输入作为命令行参数:

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

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

好一个;)执行得很好!
罗尔夫(Rolf)2015年

0

Ruby,91个字节

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

如果我更加努力,我可能会做得更好,但这是到目前为止的结果。


使用1024.代替1024.0
mbomb007


0

Ruby,90个字节

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}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.