sintax - ruby on rails haml
¿Tamaño de archivo bonito en Ruby? (6)
¿Qué tal la gema del tamaño de archivo? Parece poder convertir de bytes (y otros formatos) a valores impresos bonitos:
ejemplo:
Filesize.from("12502343 B").pretty # => "11.92 MiB"
Estoy tratando de hacer un método que convierta un entero que represente los bytes a una cadena con un formato "preparado".
Aquí está mi intento medio de trabajo:
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
¿Qué estoy haciendo mal?
Esta es mi solución:
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
Comparado con otras soluciones, es más simple, más eficiente y genera un resultado más adecuado.
Formato
Tanto to_filesize
como to_human
tienen problemas con grandes números. format_mb
tiene un caso extraño en el que, por ejemplo, ''1 MiB'' se considera ''1024 KiB'', que es algo que algunas personas pueden desear, pero ciertamente no yo.
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
Actuación
Además, tiene el mejor rendimiento.
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)
Probé cada implementación con un generador de números aleatorios realista:
def numbers
Enumerator.new do |enum|
1000000.times do
exp = rand(5)
num = rand(1024 ** exp)
enum.yield num
end
end
end
Estoy de acuerdo con @David en que probablemente sea mejor utilizar una solución existente, pero para responder a su pregunta sobre lo que está haciendo mal:
- El error primario es dividir
s
porself
lugar de al revés. - Realmente quieres dividir por la
s
anterior, entonces divides
por 1024. - Hacer aritmética de enteros le dará resultados confusos, así que conviértalos a flotar.
- Quizás redondear la respuesta.
Asi que:
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
te deja:
1.to_filesize # => "1.0B" 1020.to_filesize # => "1020.0B" 1024.to_filesize # => "1.0KB" 1048576.to_filesize # => "1.0MB"
Nuevamente, no recomiendo hacerlo, pero vale la pena corregir los errores.
La solución de @Darshan Computing es solo parcial aquí. Dado que no se garantiza que se ordenen las claves hash, este enfoque no funcionará de manera confiable. Podría arreglar esto haciendo algo como esto dentro del método 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 }
}
Esto es lo que terminé haciendo para un método similar dentro de 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
Obtiene puntos por agregar un método a Integer, pero esto parece ser más específico del archivo, por lo que sugeriría que haga monos con File, por ejemplo, agregando un método al archivo llamado .prettysize ().
Pero aquí hay una solución alternativa que utiliza la iteración y evita la impresión de bytes individuales como flotante :-)
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
Si lo usa con Rails, ¿qué pasa con el asistente de números de Rails estándar?
number_to_human_size(number, options = {})
?