2008-12-06 11 views
29

En Ruby puede hacer referencia a variables dentro de cadenas y se interpolan en tiempo de ejecución.En Ruby, ¿puede realizar la interpolación de cadenas en datos leídos desde un archivo?

Por ejemplo, si se declara una variable es igual a foo"Ted" y se declara una cadena "Hello, #{foo}" se interpola a "Hello, Ted".

No he podido averiguar cómo realizar la interpolación mágica "#{}" en datos leídos de un archivo.

En pseudo código que podría ser algo como esto:

interpolated_string = File.new('myfile.txt').read.interpolate 

Pero ese último interpolate método no existe.

Respuesta

18

En lugar de interpolar, puede usar erb. This blog da ejemplo sencillo de uso ERB,

require 'erb' 
name = "Rasmus" 
template_string = "My name is <%= name %>" 
template = ERB.new template_string 
puts template.result # prints "My name is Rasmus" 

Kernel#eval podrían utilizarse, también. Pero la mayoría de las veces desea utilizar un sistema de plantilla simple como erb.

+1

O tal vez usar algo así como líquido sería más seguro. Es el mismo concepto que erb sin la posibilidad de que los usuarios maliciosos dañen su aplicación. –

+1

El uso de eval puede representar un gran riesgo para la seguridad y no se recomienda a menos que usted confíe en el contenido del archivo. – thesmart

20

Bueno, en segundo lugar la respuesta de stesch de usar erb en esta situación. Pero puedes usar eval como este. Si tiene datos.txt contenidos:

he #{foo} he 

A continuación, puede cargar e interpolar así:

str = File.read("data.txt") 
foo = 3 
result = eval("\"" + str + "\"") 

Y result habrá:

"he 3 he" 
+0

y como siempre, tenga cuidado con la evaluación – rampion

+0

rampion es correcto. Y esto es importante con cada idioma que tiene esa característica. – stesch

+1

Probablemente sea una buena idea, al menos, hacer un primer paso al escapar de las comillas en 'str', por lo que la evaluación debe ser:' eval ('"' + str.gsub (/" /, '\ "') + ' "')' – Kelan

5

Los 2 respuestas más obvias ya han sido dar, pero si no lo hacen por alguna razón, existe el operador de formato:

>> x = 1 
=> 1 
>> File.read('temp') % ["#{x}", 'saddle'] 
=> "The number of horses is 1, where each horse has a saddle\n" 

donde en lugar de la magia # {} tiene el más viejo (pero la prueba del tiempo)% s magia ...

21

Creo que esto podría ser la forma más fácil y segura de hacer lo que quiera en Ruby 1.9. x (sprintf no admite la referencia por nombre en 1.8.x): use la función Kernel.sprintf de "referencia por nombre". Ejemplo:

>> mystring = "There are %{thing1}s and %{thing2}s here." 
=> "There are %{thing1}s and %{thing2}s here." 

>> vars = {:thing1 => "trees", :thing2 => "houses"} 
=> {:thing1=>"trees", :thing2=>"houses"} 

>> mystring % vars 
=> "There are trees and houses here." 
+0

La mejor respuesta. No tan pesado como erb, no tan arriesgado como 'eval'. – wberry

7

Rubí Facetas proporciona un método String#interpolate:

Interpolar. Proporciona un medio de extenally usando el mechismo de interpolación Ruby string .

try = "hello" 
str = "\#{try}!!!" 
String.interpolate{ str } #=> "hello!!!" 

NOTA: El bloque lo necesite con el fin de obtener continuación unión de la persona que llama.

+2

Tu enlace está muerto. También esto, en ruby ​​2.0.0 obtuve un método indefinido para el método 'interpolar' en la clase 'String'. – adamwong246

+1

De acuerdo, 'interpolate' se ha ido. –

9

Puede leer el archivo en una cadena usando IO.leer (nombre de archivo), y luego usar el resultado como una cadena de formato (http://www.ruby-doc.org/core-2.0/String.html#method-i-25):

miarchivo.txt:

My name is %{firstname} %{lastname} and I am here to talk about %{subject} today. 

fill_in_name.rb:

sentence = IO.read('myfile.txt') % { 
    :firstname => 'Joe', 
    :lastname => 'Schmoe', 
    :subject => 'file interpolation' 
} 
puts sentence 

resultado de la ejecución "ruby fill_in_name .rb" en la terminal:

My name is Joe Schmoe and I am here to talk about file interpolation today. 
0

Usando daniel-lucraft 's respuesta como mi b ase (ya que parece ser el único que respondió la pregunta) decidí resolver este problema de una manera robusta. A continuación encontrará el código para esta solución.

# encoding: utf-8 

class String 
    INTERPOLATE_DELIMETER_LIST = [ '"', "'", "\x02", "\x03", "\x7F", '|', '+', '-' ] 
    def interpolate(data = {}) 
    binding = Kernel.binding 

    data.each do |k, v| 
     binding.local_variable_set(k, v) 
    end 

    delemeter = nil 
    INTERPOLATE_DELIMETER_LIST.each do |k| 
     next if self.include? k 
     delemeter = k 
     break 
    end 
    raise ArgumentError, "String contains all the reserved characters" unless delemeter 
    e = s = delemeter 
    string = "%Q#{s}" + self + "#{e}" 
    binding.eval string 
    end 
end 

output = 
begin 
    File.read("data.txt").interpolate(foo: 3) 
rescue NameError => error 
    puts error 
rescue ArgumentError => error 
    puts error 
end 

p output 

para la entrada

he #{foo} he 

que obtener la salida

"he 3 he" 

La entrada

"he #{bad} he\n" 

provocará una excepción NameError. Y la entrada

"\"'\u0002\u0003\u007F|+-" 

se elevará y ArgumentError excepción quejándose de que la entrada contiene todos los caracteres delimitadores disponibles.

0

También podría arrojar mi propia solución a la mezcla.

irb(main):001:0> str = '#{13*3} Music' 
=> "\#{13*3} Music" 
irb(main):002:0> str.gsub(/\#\{(.*?)\}/) { |match| eval($1) } 
=> "39 Music" 

La debilidad es que la expresión que desea evaluar puede tener más {} en ella, por lo que la expresión regular probablemente debería mejorarse.

Cuestiones relacionadas