我正在尝试创建一种方法,将表示字节的整数转换为具有“漂亮”格式的字符串。
这是我的半工作尝试:
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
我做错了什么?
如果您将其与 Rails 一起使用 - 标准 Rails 数字助手怎么样?
number_to_human_size(number, options = {})
?
Filesize gem 怎么样?它似乎能够从字节(和其他格式)转换为漂亮的打印值:
示例:
Filesize.from("12502343 B").pretty # => "11.92 MiB"
我同意@David的观点,最好使用现有的解决方案,但要回答你关于你做错了什么的问题:
s
除以self
,而不是相反。s
,因此将 s
除以 1024。所以:
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”
再次,我不建议实际这样做,但似乎值得纠正错误。
这是我的解决方案:
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
这是使用
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'
您可以通过向 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
FileSize 可能已死,但现在有了 ByteSize。
require 'bytesize'
ByteSize.new(1210000000) #=> (1.21 GB)
ByteSize.new(1210000000).to_s #=> 1.21 GB
@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
不幸的是,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 默认加载最小依赖项感谢自动加载机制,这就是为什么你需要这个双重要求。