template sintax rails escape comment ruby integer byte filesize

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"

http://rubygems.org/gems/filesize

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:

  1. El error primario es dividir s por self lugar de al revés.
  2. Realmente quieres dividir por la s anterior, entonces divide s por 1024.
  3. Hacer aritmética de enteros le dará resultados confusos, así que conviértalos a flotar.
  4. 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