使用索引数组作为值计算字符串

时间:2015-11-29 18:01:09

标签: ruby regex

我想获取一个包含位置参数标记(未命名)的字符串,为其提供值的数组(而非哈希),并对其进行评估。

用例作为示例有点像ARGV。

例如,

# given:
string = "echo $1 ; echo $@"
values = ["hello", "world"]

# expected result:
"echo hello ; echo hello world"

以下功能是我能想到的最好的功能:

def evaluate_args(string, arguments)
  return string unless arguments.is_a? Array and !arguments.empty?

  # Create a variable that can replace $@ with all arguments, and quote
  # arguments that had "more than one word" originally
  all_arguments = arguments.map{|a| a =~ /\s/ ? "\"#{a}\"" : a}.join ' '

  # Replace all $1 - $9 with their respective argument ($1 ==> arguments[0])
  string.gsub!(/\$(\d)/) { arguments[$1.to_i - 1] }

  # Replace $@ or $* with all arguments
  string.gsub!(/\$[*|@]/, all_arguments)

  return string
end

在我看来,它似乎可以而且应该更简单。

我希望能找到更接近Kernel.sprintf做事方法的东西 - 比如"string with %{marker}" % {marker: 'value'}

所以,虽然这个问题几乎已经解决了(我认为),但我很想知道是否有一些我错过的东西可以让它更优雅。

2 个答案:

答案 0 :(得分:2)

您似乎正在尝试重现Bash风格的变量扩展,这是一个非常复杂的问题。但至少,您可以通过两种方式简化代码:

  1. 使用Kernel.sprintf的内置位置参数功能。以下代码通过替换例如$1 sprintf等效%1$s
  2. 使用标准库中的Shellwords来转义带空格的参数等。
  3. require 'shellwords'
    
    def evaluate_args(string, arguments)
      return string unless arguments.is_a? Array and !arguments.empty?
      tmpl = string.gsub(/\$(\d+)/, '%\1$s')
      (tmpl % arguments).gsub(/\$[*@]/, arguments.shelljoin)
    end
    
    string = "echo $1 ; echo $@"
    values = ["hello", "world"]
    
    puts evaluate_args(string, values)
    # => echo hello ; echo hello world
    

    如果您没有$*要求,我建议您只删除类似Bash的格式并使用sprintf,因为它涵盖了您提到的其他所有内容。即便如此,您还可以使用sprintf格式化其他所有内容来进一步简化操作:

    def evaluate_args(string, arguments)
      return string unless arguments.is_a? Array and !arguments.empty?
      string.gsub('%@', arguments.shelljoin) % arguments
    end
    
    string = "echo %1$s ; echo %@"
    values = ["hello", "world"]
    
    puts evaluate_args(string, values)
    # => echo hello ; echo hello world
    

    修改

    如果要将%{1}sprintf一起使用,可以将输入数组转换为散列,其中整数索引转换为符号键,例如["hello", "world"]变为{ :"1" => "hello", :"2" => "world" }

    require "shellwords"
    
    def evaluate_args(string, arguments)
      return string unless arguments.is_a? Array and !arguments.empty?
      string % {
        :* => arguments.shelljoin,
        **arguments.map.with_index {|val,idx| [ :"#{idx + 1}", val ] }.to_h
      }
    end
    
    string = "echo %{1} ; echo %{*}"
    values = ["hello", "world"]
    
    puts evaluate_args(string, values)
    # => echo hello ; echo hello world
    

答案 1 :(得分:1)

string = "echo $1 ; echo $@ ; echo $2 ; echo $cat"
values = ["hello", "World War II"]

vals = values.map { |s| s.include?(' ') ? "\"#{s}\"" : s }
  #=> ["hello", "\"World War II\""]
all_vals = vals.join(' ')
  #=> "hello \"World War II\"" 
string.gsub(/\$\d+|\$[@*]/) { |s| s[/\$\d/] ? vals[s[1..-1].to_i-1] : all_vals }
  #=> "echo hello ; echo hello \"World War II\" ; echo \"World War II\" ; echo $cat" $cat"