2008-12-06 8 views
29

In Ruby können Sie Variablen innerhalb von Strings referenzieren und diese werden zur Laufzeit interpoliert.Können Sie in Ruby eine String-Interpolation für Daten ausführen, die aus einer Datei gelesen werden?

Zum Beispiel, wenn Sie deklarieren eine Variable foo gleich "Ted" und erklären Sie, einen String "Hello, #{foo}" es "Hello, Ted" interpoliert.

Ich konnte nicht herausfinden, wie die magische "#{}" Interpolation auf Daten aus einer Datei gelesen werden.

In Pseudo-Code könnte es etwa so aussehen:

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

Aber die letzte interpolate Methode existiert nicht.

Antwort

18

Anstatt zu interpolieren, könnten Sie erb verwenden. This blog gibt einfaches Beispiel für ERB-Nutzung,

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 verwendet werden könnte, auch. Aber die meiste Zeit möchten Sie ein einfaches Vorlagensystem wie erb verwenden.

+1

Oder vielleicht mit etwas wie Flüssigkeit wäre sicherer. Es ist das gleiche Konzept wie erb ohne die Fähigkeit böswilliger Benutzer, Ihre Anwendung zu beschädigen. –

+1

Die Verwendung von eval kann ein großes Sicherheitsrisiko darstellen und wird nicht empfohlen, es sei denn, Sie vertrauen dem Inhalt der Datei. – thesmart

20

Nun, ich zweite Steps Antwort der Verwendung von Erb in dieser Situation. Aber Sie können Eval wie folgt verwenden. Wenn data.txt hat Inhalt:

he #{foo} he 

Dann können Sie wie folgt laden und zu interpolieren:

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

Und result sein wird:

"he 3 he" 
+0

und wie immer, seien Sie vorsichtig mit Ihrem eval – rampion

+0

rampion ist richtig. Und das ist wichtig für jede Sprache, die eine solche Funktion hat. – stesch

+1

Es ist wahrscheinlich eine gute Idee, zumindest einen ersten Pass zu machen, um alle Anführungszeichen in 'str' zu entfernen, also sollte eval lauten:' eval ('' '+ str.gsub (/ "/,' \" ') +' "')' – Kelan

5

Die 2 naheliegendste Antworten wurden bereits geben, aber wenn sie es aus irgendeinem Grund nicht tun, gibt es das Format Operator:

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

wo anstelle der # {} Magie Sie die ältere (aber altbewährte)% s magic ...

21

Ich denke, dies könnte die einfachste und sicherste Weg zu tun, was Sie wollen in Ruby 1.9 haben. x (sprintf unterstützt keinen Verweis auf den Namen in 1.8.x): Verwenden Sie die Kernel.sprintf-Funktion von "reference by name". Beispiel:

>> 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

Beste Antwort. Nicht so schwer wie erb, nicht so riskant wie 'eval'. – wberry

7

Rubin facettiert stellt eine String#interpolate Methode:

interpolieren. Bietet ein Mittel von extern Ruby String Interpolation Mechinism.

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

HINWEIS: Die notwendigen Block um dann des Anrufers erhalten verbindlich.

+2

Ihr Link ist tot. Auch dies, in Ruby 2.0.0 habe ich eine undefinierte Methode für die Methode 'interpolieren' in der Klasse 'String'. – adamwong246

+1

Einverstanden - 'interpolieren' ist weg. –

9

Sie können die Datei mit IO in eine Zeichenfolge einlesen.lesen (Dateiname), und verwenden Sie dann das Ergebnis als Format-String (http://www.ruby-doc.org/core-2.0/String.html#method-i-25):

myfile.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 

Ergebnis der Ausführung „ruby fill_in_name .rb“am Terminal:

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

Mit daniel-lucraft‚s Antwort als meine b ase (da er der einzige zu sein scheint, der die Frage beantwortete), beschloss ich, dieses Problem auf eine robuste Art und Weise zu lösen. Im Folgenden finden Sie den Code für diese Lösung.

# 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 

für die Eingabe

he #{foo} he 

erhalten Sie die Ausgabe

"he 3 he" 

Der Eingang

"he #{bad} he\n" 

eine Nameerror Ausnahme ausgelöst. Und die Eingabe

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

erhöhen wird und Argument Ausnahme beschwert, dass die Eingabe alle verfügbaren Trennzeichen enthalten.

0

Kann auch meine eigene Lösung in den Mix werfen.

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

Die Schwäche ist, dass der Ausdruck, den Sie weiter auswerten möchten kann {} darin, so die Regex wahrscheinlich verbessert werden soll.

Verwandte Themen