ruby-on-rails - Ruby:解析、替换和评估字符串公式

标签 ruby-on-rails ruby parsing parameters eval

我正在为 friend 的心理调查项目创建一个简单的 Ruby on Rails 调查应用程序。 所以我们有调查,每个调查都有一堆问题,每个问题都有一个参与者可以选择的选项。没有什么令人兴奋的。

其中一个有趣的方面是每个答案选项都有一个与之关联的分数值。 因此,对于每项调查,需要根据这些值计算总分。

现在我的想法是允许用户添加一个公式来计算总调查分数,而不是硬编码计算。示例公式:

"Q1 + Q2 + Q3"
"(Q1 + Q2 + Q3) / 3"
"(10 - Q1) + Q2 + (Q3 * 2)"

所以只是基础数学(为了清楚起见,有一些额外的括号)。我们的想法是让公式非常简单,这样任何具有基本数学知识的人都可以输入它们,而无需解析一些花哨的语法。

我的想法是采用任何给定的公式,并根据参与者的选择将 Q1、Q2 等占位符替换为分数值。然后 eval() 新形成的字符串。像这样:

f = "(Q1 + Q2 + Q3) / 2"  # some crazy formula for this survey
values = {:Q1 => 1, :Q2 => 2, :Q3 => 2}  # values for substitution 
result = f.gsub(/(Q\d+)/) {|m| values[$1.to_sym] }   # string to be eval()-ed
eval(result)

所以我的问题是:

  1. 有更好的方法吗? 我愿意接受任何建议。

  2. 如何处理不是所有的公式 占位符被成功替换(例如一个 问题没有回答)?例如:{:Q2 => 2} 不是 在值哈希?我的想法是挽救 eval() 但在这种情况下它不会失败,因为 (1++ 2)/2 仍然可以被 eval() 编辑...有什么想法吗?

  3. 如何得到正确的结果?应该是 2.5,但由于整数运算,它将截断为 2。我不能指望提供正确公式(例如/2.0 )的人理解这种细微差别。

  4. 我不希望这样,但是如何 最好地保护 eval() 免受滥用(例如 错误的公式,操纵值 进来)?示例:f = 'system("ruby -v"); (Q1 + (Q2/3) + Q3 + (Q4 * 2))/2 '

谢谢!

最佳答案

好的,现在完全安全了。我发誓!

我通常会克隆 formula 变量,但在这种情况下,由于您担心恶意用户,我就地清理了该变量:

class Evaluator

  def self.formula(formula, values)
    # remove anything but Q's, numbers, ()'s, decimal points, and basic math operators 
    formula.gsub!(/((?![qQ0-9\s\.\-\+\*\/\(\)]).)*/,'').upcase!
    begin
      formula.gsub!(/Q\d+/) { |match|
        ( 
          values[match.to_sym] && 
          values[match.to_sym].class.ancestors.include?(Numeric) ?
          values[match.to_sym].to_s :
          '0'
        )+'.0'
      }
      instance_eval(formula)
    rescue Exception => e
      e.inspect
    end
  end

end

f = '(q1 + (q2 / 3) + q3 + (q4 * 2))'  # some crazy formula for this survey
values = {:Q2 => 1, :Q4 => 2}  # values for substitution 
puts "formula: #{f} = #{Evaluator.formula(f,values)}"  
=> formula: (0.0 + (1.0 / 3) + 0.0 + (2.0 * 2)) = 4.333333333333333

f = '(Q1 + (Q2 / 3) + Q3 + (Q4 * 2)) / 2'  # some crazy formula for this survey
values = {:Q1 => 1, :Q3 => 2}  # values for substitution 
puts "formula: #{f} = #{Evaluator.formula(f,values)}"  
=> formula: (1.0 + (0.0 / 3) + 2.0 + (0.0 * 2)) / 2 = 1.5

f = '(Q1 + (Q2 / 3) + Q3 + (Q4 * 2)) / 2'  # some crazy formula for this survey
values = {:Q1 => 'delete your hard drive', :Q3 => 2}  # values for substitution 
puts "formula: #{f} = #{Evaluator.formula(f,values)}"  
=> formula: (0.0 + (0.0 / 3) + 2.0 + (0.0 * 2)) / 2 = 1.0

f = 'system("ruby -v")'  # some crazy formula for this survey
values = {:Q1 => 'delete your hard drive', :Q3 => 2}  # values for substitution 
puts "formula: #{f} = #{Evaluator.formula(f,values)}"  
=> formula: ( -) = #<SyntaxError: (eval):1: syntax error, unexpected ')'>

关于ruby-on-rails - Ruby:解析、替换和评估字符串公式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4880886/

相关文章:

ruby-on-rails - Ruby On Rails,周数不正确 (-1)

ruby-on-rails - 如何在 YAML 文件中使用变量?

ruby-on-rails - 当我想要 rails 中的全局对象时,如何避免常量?

python - 没有名为 ijson 的模块

ruby-on-rails - 使用Capistrano部署时出错

ruby-on-rails - 创建到 localhost postgres 服务器的连接

android - 如何通过解析从xml数据中获取图像

json - 在 Dart 中解析 JSON

ruby-on-rails - 如何将相同的 url 路由到不同的操作?

ruby - Nokogiri/Mechanize xpath 定位器在存在杂散开始标记时中断