2008-12-06 4 views
29

В Ruby вы можете ссылаться на переменные внутри строк, и они интерполируются во время выполнения.В Ruby вы можете выполнять строчную интерполяцию данных, считанных из файла?

Например, если вы объявляете переменную foo равна "Ted" и вы объявляете строку "Hello, #{foo}" она интерполирует в "Hello, Ted".

Я не смог вычислить, как выполнить магию "#{}" интерполяция на данные, считанные из файла.

В псевдокоде это может выглядеть примерно так:

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

Но последний interpolate метод не существует.

ответ

18

Вместо интерполяции вы можете использовать erb. This blog дает простой пример использования 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 может быть использован, тоже. Но большую часть времени вы хотите использовать простую систему шаблонов, например, erb.

+1

Или, возможно, что-то вроде жидкости будет безопаснее. Это та же концепция, что и erb, без возможности злоумышленников вредить вашему приложению. – 2010-05-06 03:02:03

+1

Использование eval может создать огромный риск для безопасности и не рекомендуется, если вы не доверяете содержимому файла. – thesmart 2014-02-10 22:32:45

20

Ну, я отвечу на второй вопрос от использования erb в этой ситуации. Но вы можете использовать eval так. Если data.txt имеет содержание:

he #{foo} he 

Затем вы можете загрузить и интерполировать так:

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

И result будет:

"he 3 he" 
+0

и, как всегда, будьте осторожны с вашим лифтом eval's – rampion 2008-12-06 17:53:59

+0

. И это важно на каждом языке, который имеет такую ​​особенность. – stesch 2008-12-06 18:47:59

5

2 наиболее очевидные ответы уже дайте, но если они не по каким-либо причинам, то есть оператор формата:

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

где вместо # {} магий у вас есть старый (но проверенное время)% волшебство ...

21

Я думаю, что это может быть самым простым и безопасным способом делать то, что вы хотите в Ruby 1.9. x (sprintf не поддерживает ссылку по имени в 1.8.x): используйте функцию Kernel.sprintf «reference by name». Пример:

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

Рубин Грани обеспечивает String#interpolate метод:

Interpolate. Предоставляет средство с использованием Ruby string интерполяционный мехинизм.

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

Примечание: Блок для того, необходимых, чтобы получить затем связывание вызывающего абонента.

9

Вы можете прочитать файл в строке с использованием ввода-вывода.чтения (имя файла), а затем использовать результат в виде строки формата (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 

результат работы «рубин fill_in_name .rb»на терминале:

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

Используя ответ daniel-lucraft«s как мой б ase (поскольку он, кажется, единственный, кто ответил на вопрос), я решил решительно решить эту проблему. Ниже вы найдете код для этого решения.

# 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 

для ввода

he #{foo} he 

вы получите выходного

"he 3 he" 

Входные сигналы

"he #{bad} he\n" 

возбудит исключение NameError. И вход

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

подниму и ArgumentError исключение жалуются, что входные данные содержали все доступные символы-разделители.

0

Можете также бросить мое собственное решение в микс.

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

Недостаток заключается в том, что выражение, которое вы хотите оценить, может иметь в нем {}, поэтому регулярное выражение, вероятно, должно быть улучшено.