【问题标题】:In Ruby, can you perform string interpolation on data read from a file?在 Ruby 中,您可以对从文件中读取的数据执行字符串插值吗?
【发布时间】:2010-09-25 16:09:06
【问题描述】:

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

例如,如果您声明一个变量foo 等于"Ted",并且您声明一个字符串"Hello, #{foo}",它会内插到"Hello, Ted"

我无法弄清楚如何对从文件读取的数据执行神奇的"#{}" 插值。

在伪代码中它可能看起来像这样:

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

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

【问题讨论】:

    标签: ruby string interpolation


    【解决方案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 %{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." 
      

      【讨论】:

      • 最佳答案。没有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"
          

          缺点是您要评估的表达式可能包含进一步的 { },因此可能应该改进正则表达式。

          【讨论】:

          • 非常适合简单的上下文。
          猜你喜欢
          • 2018-09-17
          • 1970-01-01
          • 2017-02-13
          • 1970-01-01
          • 2019-02-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多