Ruby中的漂亮文件大小?

我正在尝试创建一个方法,将表示字节的整数转换为具有’prettied up’格式的字符串。

这是我的半工作尝试:

class Integer def to_filesize { 'B' => 1024, 'KB' => 1024 * 1024, 'MB' => 1024 * 1024 * 1024, 'GB' => 1024 * 1024 * 1024 * 1024, 'TB' => 1024 * 1024 * 1024 * 1024 * 1024 }.each_pair { |e, s| return "#{s / self}#{e}" if self < s } end end 

我究竟做错了什么?

Filesize gem怎么样? 它似乎能够从字节(和其他格式)转换为漂亮的打印值:

例:

 Filesize.from("12502343 B").pretty # => "11.92 MiB" 

http://rubygems.org/gems/filesize

如果你在Rails中使用它 – 那么标准的Rails编号助手呢?

http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html#method-i-number_to_human_size

number_to_human_size(number, options = {})

我同意@David认为最好使用现有的解决方案,但回答你关于你做错的问题:

  1. 主要错误是将s除以self而不是相反。
  2. 你真的想要除以前面的s ,所以除以1024。
  3. 进行整数运算会给你带来令人困惑的结果,所以转换为float。
  4. 也许围绕着答案。

所以:

 class Integer def to_filesize { 'B' => 1024, 'KB' => 1024 * 1024, 'MB' => 1024 * 1024 * 1024, 'GB' => 1024 * 1024 * 1024 * 1024, 'TB' => 1024 * 1024 * 1024 * 1024 * 1024 }.each_pair { |e, s| return "#{(self.to_f / (s / 1024)).round(2)}#{e}" if self < s } end end 

让你:

 1.to_filesize
 #=>“1.0B”
 1020.to_filesize
 #=>“1020.0B” 
 1024.to_filesize
 #=>“1.0KB” 
 1048576.to_filesize
 #=>“1.0MB”

同样,我不建议实际这样做,但似乎值得纠正错误。

你得到了一个向Integer添加方法的点,但是这看起来更像File,所以我建议使用File进行修改,比如在File中添加一个名为.prettysize()的方法。

但是这里有一个使用迭代的替代解决方案,并避免将单个字节打印为float 🙂

 def format_mb(size) conv = [ 'b', 'kb', 'mb', 'gb', 'tb', 'pb', 'eb' ]; scale = 1024; ndx=1 if( size < 2*(scale**ndx) ) then return "#{(size)} #{conv[ndx-1]}" end size=size.to_f [2,3,4,5,6,7].each do |ndx| if( size < 2*(scale**ndx) ) then return "#{'%.3f' % (size/(scale**(ndx-1)))} #{conv[ndx-1]}" end end ndx=7 return "#{'%.3f' % (size/(scale**(ndx-1)))} #{conv[ndx-1]}" end 

这是我的解决方案:

 def filesize(size) units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'Pib', 'EiB'] return '0.0 B' if size == 0 exp = (Math.log(size) / Math.log(1024)).to_i exp = 6 if exp > 6 '%.1f %s' % [size.to_f / 1024 ** exp, units[exp]] end 

与其他解决方案相比,它更简单,更高效,并产生更合适的输出。

格式

to_filesizeto_human都有大数字问题。 format_mb有一个奇怪的例子,例如’1 MiB’被认为是’1024 KiB’,这是某些人可能想要的,但肯定不是我。

  origin: filesize to_filesize format_mb to_human 0 B: 0.0 B 0.0B 0 b 0.00 B 1 B: 1.0 B 1.0B 1 b 1.00 B 10 B: 10.0 B 10.0B 10 b 10.00 B 1000 B: 1000.0 B 1000.0B 1000 b 1000.00 B 1 KiB: 1.0 KiB 1.0KB 1024 b 1.00 KB 1.5 KiB: 1.5 KiB 1.5KB 1536.0 b 1.50 KB 10 KiB: 10.0 KiB 10.0KB 10.000 kb 10.00 KB 100 KiB: 100.0 KiB 100.0KB 100.000 kb 100.00 KB 1000 KiB: 1000.0 KiB 1000.0KB 1000.000 kb 1000.00 KB 1 MiB: 1.0 MiB 1.0MB 1024.000 kb 1.00 MB 1 Gib: 1.0 GiB 1.0GB 1024.000 mb 1.00 GB 1 TiB: 1.0 TiB 1.0TB 1024.000 gb 1.00 TB 1 PiB: 1.0 Pib ERROR 1024.000 tb 1.00 PB 1 EiB: 1.0 EiB ERROR 1024.000 pb 1.00 EB 1 ZiB: 1024.0 EiB ERROR 1024.000 eb ERROR 1 YiB: 1048576.0 EiB ERROR 1048576.000 eb ERROR 

性能

此外,它具有最佳性能。

  user system total real filesize: 2.740000 0.000000 2.740000 ( 2.747873) to_filesize: 3.560000 0.000000 3.560000 ( 3.557808) format_mb: 2.950000 0.000000 2.950000 ( 2.949930) to_human: 5.770000 0.000000 5.770000 ( 5.783925) 

我用一个真实的随机数生成器测试了每个实现:

 def numbers Enumerator.new do |enum| 1000000.times do exp = rand(5) num = rand(1024 ** exp) enum.yield num end end end 

@Darshan Computing的解决方案仅限于此。 由于无法保证对哈希键进行排序,因此该方法无法可靠地工作。 您可以通过在to_filesize方法中执行类似的操作来解决此问题,

  conv={ 1024=>'B', 1024*1024=>'KB', ... } conv.keys.sort.each { |s| next if self >= s e=conv[s] return "#{(self.to_f / (s / 1024)).round(2)}#{e}" if self < s } } 

这就是我最终为Float中的类似方法所做的事情,

  class Float def to_human conv={ 1024=>'B', 1024*1024=>'KB', 1024*1024*1024=>'MB', 1024*1024*1024*1024=>'GB', 1024*1024*1024*1024*1024=>'TB', 1024*1024*1024*1024*1024*1024=>'PB', 1024*1024*1024*1024*1024*1024*1024=>'EB' } conv.keys.sort.each { |mult| next if self >= mult suffix=conv[mult] return "%.2f %s" % [ self / (mult / 1024), suffix ] } end end 
Interesting Posts