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

h79rfbju  于 2023-04-11  发布在  Ruby
关注(0)|答案(9)|浏览(131)

在Ruby中,你可以在字符串中引用变量,它们在运行时被插值。
例如,如果你声明一个变量foo等于"Ted",你声明一个字符串"Hello, #{foo}",它会插值到"Hello, Ted"
我还没有弄清楚如何对从文件中读取的数据执行神奇的"#{}"插值。
在伪代码中,它可能看起来像这样:

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

但是最后一个interpolate方法并不存在。

xzabzqsa

xzabzqsa1#

我认为这可能是在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."
drnojrws

drnojrws2#

好吧,我同意stesch在这种情况下使用erb的答案。但是你可以这样使用eval。如果data.txt有内容:

he #{foo} he

然后你可以像这样加载和插值:

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

result将是:

"he 3 he"
uelo1irk

uelo1irk3#

可以使用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这样的简单模板系统。

eeq64g8w

eeq64g8w4#

Ruby Facets提供了一个String#interpolate方法:
提供了一种扩展使用Ruby字符串插值机制的方法。

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

注意:为了获得调用者的绑定,这个块是必需的。

30byixjq

30byixjq5#

您可以使用www.example.com(filename)将文件读入字符串IO.read,然后将结果用作格式字符串(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

在终端运行“ruby fill_in_name.rb”的结果:

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

dced5bon6#

已经给出了两个最明显的答案,但是如果他们出于某种原因不这样做,还有格式操作符:

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

这里的#{}魔法已被旧的(但经过时间考验的)%s魔法取代...

roejwanj

roejwanj7#

使用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异常,抱怨输入包含所有可用的分隔符字符。

w9apscun

w9apscun8#

最好把我自己的解决方案也加进去。

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

缺点是你想计算的表达式可能有进一步的{ },所以正则表达式可能需要改进。

jjhzyzn0

jjhzyzn09#

@andrewgrimm的回答是正确的,但不完整。Facets gemString扩展,包括interpolate,这正是最初的问题所要求的。

$ gem install facets

请注意require语句。

require 'facets/string/interpolate'

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

下面是一个完整的工作示例:

require 'facets/string/interpolate'

string = 'There are #{thing1}s and #{thing2}s here.'
thing1 = 'stick'
thing2 = 'stone'

puts String.interpolate { string }

显示器:

There are sticks and stones here.

相关问题