В Ruby, вы можете выполнить интерполяцию строк для данных, прочитанных из файла? - PullRequest
31 голосов
/ 06 декабря 2008

В Ruby вы можете ссылаться на переменные внутри строк, и они интерполируются во время выполнения.

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

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

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

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

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

Ответы [ 8 ]

28 голосов
/ 29 июня 2011

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

>> 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." 
22 голосов
/ 06 декабря 2008

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

he #{foo} he

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

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

А result будет:

"he 3 he"
19 голосов
/ 06 декабря 2008

Вместо интерполяции вы можете использовать erb. Этот блог дает простой пример использования 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.

9 голосов
/ 26 марта 2013

Вы можете прочитать файл в строку, используя IO.read (имя файла), а затем использовать результат как строку формата (http://www.ruby -doc.org / core-2.0 / String.html # method- я-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

результат запуска "ruby fill_in_name.rb" в терминале:

My name is Joe Schmoe and I am here to talk about file interpolation today.
8 голосов
/ 01 июля 2011

Ruby Facets обеспечивает String # интерполировать метод:

Интерполяция. Предоставляет средства экстенсивно используя строку Ruby интерполяционный механизм.

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

ПРИМЕЧАНИЕ. Блок, необходимый для получить затем привязку вызывающего.

6 голосов
/ 07 декабря 2008

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

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

где вместо магии # {} у вас есть старая (но проверенная временем) магия% s ...

1 голос
/ 27 октября 2017

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

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

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

1 голос
/ 22 августа 2016

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

# 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 с жалобой на то, что входные данные содержат все доступные символы-разделители.

...