在Ruby中,可以对从文件读取的数据执行字符串插值吗?

在Ruby中,可以对从文件读取的数据执行字符串插值吗?,ruby,string,interpolation,Ruby,String,Interpolation,在Ruby中,您可以在字符串中引用变量,并在运行时对其进行插值 例如,如果您声明一个变量foo等于“Ted”,并且声明一个字符串“Hello,{foo}”,它将插入“Hello,Ted” 我不知道如何对从文件读取的数据执行神奇的“#{}”插值 在伪代码中,它可能如下所示: interpolated_string = File.new('myfile.txt').read.interpolate str = File.read("data.txt") foo = 3 result = eval(

在Ruby中,您可以在字符串中引用变量,并在运行时对其进行插值

例如,如果您声明一个变量
foo
等于
“Ted”
,并且声明一个字符串
“Hello,{foo}”
,它将插入
“Hello,Ted”

我不知道如何对从文件读取的数据执行神奇的
“#{}”
插值

在伪代码中,它可能如下所示:

interpolated_string = File.new('myfile.txt').read.interpolate
str = File.read("data.txt")
foo = 3
result = eval("\"" + str + "\"")

但是最后一个
插值方法不存在。

您可以使用。给出了ERB使用的简单示例

require 'erb'
name = "Rasmus"
template_string = "My name is <%= name %>"
template = ERB.new template_string
puts template.result # prints "My name is Rasmus"
需要“erb”
name=“Rasmus”
template\u string=“我的名字是”
template=ERB.new template\u字符串
puts template.result打印“我的名字是Rasmus”

也可以用。但是大多数情况下,您希望使用一个简单的模板系统,如
erb

好吧,我支持stesch关于在这种情况下使用erb的回答。但是你可以这样使用eval。如果data.txt包含以下内容:

he #{foo} he
然后可以像这样加载和插值:

interpolated_string = File.new('myfile.txt').read.interpolate
str = File.read("data.txt")
foo = 3
result = eval("\"" + str + "\"")
结果将是:

"he 3 he"

已经给出了两个最明显的答案,但如果出于某种原因他们没有给出答案,则会出现格式运算符:

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

在Ruby 1.9.x(sprintf在1.8.x中不支持按名称引用)中,使用Kernel.sprintf的“按名称引用”功能,而不是使用#{}魔法,而是使用旧的(但经过时间测试的)%s魔法…

我认为这可能是最简单、最安全的方法。例如:

>> 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." 
Ruby Facets提供了一种方法:

插入。提供一种 扩展使用Ruby字符串 插值机制

注:该块是必要的,以便 然后获取调用方的绑定


您可以使用IO.read(文件名)将文件读入字符串,然后将结果用作格式字符串():

myfile.txt:

My name is %{firstname} %{lastname} and I am here to talk about %{subject} today.
在_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.
以他的答案为基础(因为他似乎是唯一回答这个问题的人),我决定以一种稳健的方式解决这个问题。下面您将找到此解决方案的代码

# 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
"\"'\u0002\u0003\u007F|+-"
你得到了输出

 "he 3 he"
输入

"he #{bad} he\n"
将引发NameError异常。以及输入

he #{foo} he
"\"'\u0002\u0003\u007F|+-"

将引发ArgumentError异常,抱怨输入包含所有可用的分隔符

不妨加入我自己的解决方案

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

缺点是您要计算的表达式中可能有进一步的{},因此正则表达式可能需要改进。

并且一如既往,小心您的求值是正确的。这对于每一种具有这种特性的语言都很重要。至少第一次转义
str
中的任何引号可能是一个好主意,因此eval应该是:
eval(“'+str.gsub(/”,“\”)+”)
这会招致利用,是一个糟糕的选择,imho。这就像脱壳,只是不要自己做。或者使用液体之类的东西会更安全。它与erb的概念相同,不允许恶意用户破坏您的应用程序。使用eval可能会带来巨大的安全风险,除非您信任文件内容,否则不建议使用eval。您的链接已失效。同样,在ruby 2.0.0中,我得到了一个未定义的方法,用于类“String”上的方法“interpolate”。同意-
interpolate
不存在了。最好的答案是。没有erb那么重,没有
eval
那么危险。非常适合简单的环境。