在 Ruby 中,您可以对从文件中读取的数据执行字符串插值吗?
Posted
技术标签:
【中文标题】在 Ruby 中,您可以对从文件中读取的数据执行字符串插值吗?【英文标题】:In Ruby, can you perform string interpolation on data read from a file? 【发布时间】:2010-09-25 16:09:06 【问题描述】:在 Ruby 中,您可以在字符串中引用变量,并在运行时对其进行插值。
例如,如果您声明一个变量foo
等于"Ted"
,并且您声明一个字符串"Hello, #foo"
,它会内插到"Hello, Ted"
。
我无法弄清楚如何对从文件读取的数据执行神奇的"#"
插值。
在伪代码中它可能看起来像这样:
interpolated_string = File.new('myfile.txt').read.interpolate
但是最后一个interpolate
方法不存在。
【问题讨论】:
【参考方案1】:您可以使用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
。
【讨论】:
或者使用液体之类的东西会更安全。它与 erb 的概念相同,但恶意用户无法破坏您的应用程序。 使用 eval 可能会带来巨大的安全风险,除非您信任文件内容,否则不建议这样做。【参考方案2】:嗯,我第二个 stesch 在这种情况下使用 erb 的答案。但是你可以像这样使用 eval 。如果data.txt有内容:
he #foo he
然后你可以像这样加载和插值:
str = File.read("data.txt")
foo = 3
result = eval("\"" + str + "\"")
而result
将是:
"he 3 he"
【讨论】:
和往常一样,小心你的评估 rampion 是对的。这对于每一种具有这种特性的语言都很重要。 至少在str
中转义任何引号可能是一个好主意,因此 eval 应该是:eval('"' + str.gsub(/"/, '\"') + '"')
这招致了剥削,是一个糟糕的选择,恕我直言。这就像 shell 逃逸——只是不要自己做。【参考方案3】:
已经给出了两个最明显的答案,但如果他们出于某种原因没有给出答案,那就是格式运算符:
>> x = 1
=> 1
>> File.read('temp') % ["#x", 'saddle']
=> "The number of horses is 1, where each horse has a saddle\n"
你有旧的(但经过时间考验的)%s 魔法而不是 # 魔法...
【讨论】:
【参考方案4】:我认为这可能是在 Ruby 1.9.x 中执行您想要的最简单和最安全的方法(sprintf 在 1.8.x 中不支持按名称引用):使用“按名称引用”的 Kernel.sprintf 功能。示例:
>> mystring = "There are %thing1s and %thing2s here."
=> "There are %thing1s and %thing2s here."
>> vars = :thing1 => "trees", :thing2 => "houses"
=> :thing1=>"trees", :thing2=>"houses"
>> mystring % vars
=> "There are trees and houses here."
【讨论】:
最佳答案。没有erb那么重,没有eval
那么危险。【参考方案5】:
Ruby Facets 提供了一个String#interpolate 方法:
插值。提供了一种手段 外部使用 Ruby 字符串 插值机制。
try = "hello"
str = "\#try!!!"
String.interpolate str #=> "hello!!!"
注意:该块是必要的,以便 然后获取调用者的绑定。
【讨论】:
您的链接已失效。同样,在 ruby 2.0.0 中,我在类 'String' 上为方法 'interpolate' 找到了一个未定义的方法。 同意 -interpolate
不见了。
该链接现在似乎可以正常工作。 ruby facets String#interpolate
方法似乎在 facets gem 中提供,而不是作为 Ruby 核心库的一部分 - 也是 available at GitHub。有source code available for the method itself,有文档【参考方案6】:
您可以使用 IO.read(filename) 将文件读入字符串,然后将结果用作格式字符串 (http://www.ruby-doc.org/core-2.0/String.html#method-i-25):
我的文件.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.
【讨论】:
【参考方案7】:使用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 异常,抱怨输入包含所有可用的分隔符。
【讨论】:
【参考方案8】:不妨将我自己的解决方案加入其中。
irb(main):001:0> str = '#13*3 Music'
=> "\#13*3 Music"
irb(main):002:0> str.gsub(/\#\(.*?)\/) |match| eval($1)
=> "39 Music"
缺点是您要评估的表达式可能包含进一步的 ,因此可能应该改进正则表达式。
【讨论】:
非常适合简单的上下文。以上是关于在 Ruby 中,您可以对从文件中读取的数据执行字符串插值吗?的主要内容,如果未能解决你的问题,请参考以下文章