Ruby 中的文件大小相当不错?

问题描述 投票:0回答:9

我正在尝试创建一种方法,将表示字节的整数转换为具有“漂亮”格式的字符串。

这是我的半工作尝试:

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

我做错了什么?

ruby integer byte filesize
9个回答
38
投票

如果您将其与 Rails 一起使用 - 标准 Rails 数字助手怎么样?

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

number_to_human_size(number, options = {})


26
投票

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

示例:

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

http://rubygems.org/gems/filesize


17
投票

我同意@David的观点,最好使用现有的解决方案,但要回答你关于你做错了什么的问题:

  1. 主要错误是将
    s
    除以
    self
    ,而不是相反。
  2. 您确实想除以之前的
    s
    ,因此将
    s
    除以 1024。
  3. 进行整数运算会给您带来混乱的结果,因此请转换为浮点数。
  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_文件大小
# =>“1020.0B”
1024.to_文件大小
# =>“1.0KB”
1048576.to_filesize
# =>“1.0MB”

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


11
投票

这是我的解决方案:

def filesize(size)
  units = %w[B KiB MiB GiB TiB Pib EiB ZiB]

  return '0.0 B' if size == 0
  exp = (Math.log(size) / Math.log(1024)).to_i
  exp += 1 if (size.to_f / 1024 ** exp >= 1024 - 0.05)
  exp = units.size - 1 if exp > units.size - 1

  '%.1f %s' % [size.to_f / 1024 ** exp, units[exp]]
end

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

格式

所有其他方法都存在报告

1023.95 bytes
错误的问题。此外,
to_filesize
只是因为大数字而出错(它返回一个数组)。

 -       method: [     filesize,     Filesize,  number_to_human,  to_filesize ]
 -          0 B: [        0.0 B,       0.00 B,          0 Bytes,         0.0B ]
 -          1 B: [        1.0 B,       1.00 B,           1 Byte,         1.0B ]
 -         10 B: [       10.0 B,      10.00 B,         10 Bytes,        10.0B ]
 -       1000 B: [     1000.0 B,    1000.00 B,       1000 Bytes,      1000.0B ]
 -        1 KiB: [      1.0 KiB,     1.00 KiB,             1 KB,        1.0KB ]
 -      1.5 KiB: [      1.5 KiB,     1.50 KiB,           1.5 KB,        1.5KB ]
 -       10 KiB: [     10.0 KiB,    10.00 KiB,            10 KB,       10.0KB ]
 -     1000 KiB: [   1000.0 KiB,  1000.00 KiB,          1000 KB,     1000.0KB ]
 -        1 MiB: [      1.0 MiB,     1.00 MiB,             1 MB,        1.0MB ]
 -        1 GiB: [      1.0 GiB,     1.00 GiB,             1 GB,        1.0GB ]
 -  1023.95 GiB: [      1.0 TiB,  1023.95 GiB,          1020 GB,    1023.95GB ]
 -        1 TiB: [      1.0 TiB,     1.00 TiB,             1 TB,        1.0TB ]
 -        1 EiB: [      1.0 EiB,     1.00 EiB,             1 EB,        ERROR ]
 -        1 ZiB: [      1.0 ZiB,     1.00 ZiB,          1020 EB,        ERROR ]
 -        1 YiB: [   1024.0 ZiB,  1024.00 ZiB,       1050000 EB,        ERROR ]

性能

而且,它具有最好的性能(处理 100 万个数字只需几秒):

 - filesize:           2.15
 - Filesize:          15.53
 - number_to_human:  139.63
 - to_filesize:        2.41

4
投票

这是使用

log10
的方法:

def number_format(d)
   e = Math.log10(d).to_i / 3
   return '%.3f' % (d / 1000 ** e) + ['', ' k', ' M', ' G'][e]
end

s = number_format(9012345678.0)
puts s == '9.012 G'

https://ruby-doc.org/core/Math.html#method-c-log10


1
投票

您可以通过向 Integer 添加方法来获得积分,但这似乎更特定于文件,因此我建议使用 File 进行操作,例如向 File 添加一个名为 .prettysize() 的方法。

但这里有一个替代解决方案,它使用迭代,并避免将单个字节打印为浮点:-)

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

1
投票

FileSize 可能已死,但现在有了 ByteSize

require 'bytesize'

ByteSize.new(1210000000)       #=> (1.21 GB)
ByteSize.new(1210000000).to_s  #=> 1.21 GB

0
投票

@DarshanComputing的解决方案在这里只是部分的。由于不能保证散列键是有序的,因此这种方法将无法可靠地工作。您可以通过在 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

0
投票

不幸的是,filesize自2018年9月起被放弃并存档,而bytesize自2021年6月起不再维护。

另一种方法是使用ActiveSupport,即:

从 Rails 框架中提取的支持库和 Ruby 核心扩展工具包。对多字节字符串、国际化、时区和测试的丰富支持。

它旨在在 Ruby on Rails 中使用,但也可以在外部使用。

确实,

NumberHelper
类提供了一个随时可用的
number_to_human_size
方法
,它正好满足我们的需要:

将数字格式化为更人性化的字节表示形式。对于向用户报告文件大小很有用。

挑选特定的主动支持功能并具有最小的占用空间,您需要执行以下操作:

# For Active Support 7+
require 'active_support' # skip this if Active Support 6-
require 'active_support/core_ext/numeric/conversions'

number_to_human_size(1_000_000_000) # => "954 MB"
number_to_human_size(23_897)        # => "23.3 KB"
number_to_human_size(1024)          # => "1 KB"
number_to_human_size(64)            # => "64 Bytes"

number_to_human_size(27_198_870_567) # => "25.3 GB"
number_to_human_size(27_198_870_567, precision: 5) # => "25.331 GB"
number_to_human_size(27_198_870_567, precision: 2, round_mode: :up) # => "26 GB"
number_to_human_size(27_198_870_567_000_000_000_000_000, separator: ',', delimiter: ' ') # => "23 000 ZB"

Active Support 默认加载最小依赖项感谢自动加载机制,这就是为什么你需要这个双重要求。

© www.soinside.com 2019 - 2024. All rights reserved.