在 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 中,您可以对从文件中读取的数据执行字符串插值吗?的主要内容,如果未能解决你的问题,请参考以下文章

熬夜整理的C/C++万字总结,文件操作

从 ruby​​ 中的 pdf 文件中获取元数据

在 Ruby 中读取文件的第一行

如何使用 Ruby 读取和写入同一个 EXCEL 文件?

taglib(-ruby) 无法读取特定文件

Ruby脚本逐行读取文件并使用puts执行if语句?