2008-12-06 19 views
29

Trong Ruby bạn có thể tham chiếu các biến bên trong các chuỗi và chúng được nội suy trong thời gian chạy.Trong Ruby, bạn có thể thực hiện nội suy chuỗi trên dữ liệu đọc từ một tệp không?

Ví dụ: nếu bạn khai báo biến số foo bằng "Ted" và bạn khai báo một chuỗi "Hello, #{foo}", nó sẽ nội suy thành "Hello, Ted".

Tôi đã không thể tìm ra cách thực hiện phép nội suy ma thuật "#{}" trên dữ liệu được đọc từ tệp.

Trong mã giả nó có thể trông giống như thế này:

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

Nhưng đó interpolate Phương pháp cuối cùng không tồn tại.

Trả lời

18

Thay vì nội suy, bạn có thể sử dụng erb. This blog cho ví dụ đơn giản của việc sử dụng 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 có thể được sử dụng, quá. Nhưng hầu hết thời gian bạn muốn sử dụng một hệ thống mẫu đơn giản như erb.

+1

Hoặc có thể sử dụng một thứ gì đó như chất lỏng sẽ an toàn hơn. Đó là khái niệm tương tự như erb mà không có khả năng cho người dùng độc hại làm hỏng ứng dụng của bạn. –

+1

Sử dụng eval có thể gây ra rủi ro bảo mật rất lớn và không được khuyến nghị trừ khi bạn tin tưởng vào nội dung của tệp. – thesmart

20

Vâng, câu trả lời thứ hai của tôi về việc sử dụng erb trong tình huống này. Nhưng bạn có thể sử dụng eval như thế này. Nếu data.txt có nội dung:

he #{foo} he 

Sau đó, bạn có thể tải và suy như thế này:

str = File.read("data.txt") 
foo = 3 
result = eval("\"" + str + "\"") 

result sẽ là:

"he 3 he" 
+0

và như mọi khi, hãy cẩn thận với cuộc tấn công – rampion

+0

của eval của bạn là đúng. Và điều này quan trọng với mọi ngôn ngữ có tính năng như vậy. – stesch

+1

Có lẽ một ý tưởng hay là ít nhất phải thực hiện một lần thoát đầu tiên để thoát khỏi bất kỳ dấu ngoặc kép nào trong 'str', vì vậy eval phải là: 'eval ('" '+ str.gsub (/ "/,' \" ') +' "')' – Kelan

5

2 câu trả lời rõ ràng nhất đã được đưa ra, nhưng nếu họ không vì lý do nào đó, có nhà điều hành định dạng:

>> x = 1 
=> 1 
>> File.read('temp') % ["#{x}", 'saddle'] 
=> "The number of horses is 1, where each horse has a saddle\n" 

nơi thay vì phép thuật # {} bạn có ma thuật cũ hơn (nhưng được thử nghiệm)% ...

21

Tôi nghĩ đây có thể là cách dễ nhất và an toàn nhất để thực hiện những gì bạn muốn trong Ruby 1.9. x (sprintf không hỗ trợ tham chiếu theo tên trong 1.8.x): sử dụng tính năng Kernel.sprintf của "tham chiếu theo tên". Ví dụ:

>> 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." 
+0

Câu trả lời hay nhất. Không nặng như erb, không nguy hiểm như 'eval'. – wberry

7

của Ruby khía cạnh cung cấp một phương pháp String#interpolate:

suy. Cung cấp phương tiện mở rộng bằng cách sử dụng chuỗi Ruby cơ chế nội suy.

try = "hello" 
str = "\#{try}!!!" 
String.interpolate{ str } #=> "hello!!!" 

Chú ý: cần thiết khối để có được sau đó ràng buộc của người gọi.

+2

Liên kết của bạn đã chết. Ngoài ra, trong ruby ​​2.0.0 tôi có một phương thức không xác định cho phương thức 'nội suy' trên lớp 'Chuỗi'. – adamwong246

+1

Đồng ý - 'nội suy' biến mất. –

9

Bạn có thể đọc tệp thành chuỗi bằng IO.đọc (filename), và sau đó sử dụng kết quả như là một chuỗi định dạng (http://www.ruby-doc.org/core-2.0/String.html#method-i-25):

myfile.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 

kết quả của chạy "ruby fill_in_name .rb "tại nhà ga:

My name is Joe Schmoe and I am here to talk about file interpolation today. 
0

Sử dụng câu trả lời của daniel-lucraft làm b ase (như anh ta có vẻ là người duy nhất trả lời câu hỏi) Tôi quyết định giải quyết vấn đề này một cách mạnh mẽ. Dưới đây bạn sẽ tìm thấy mã cho giải pháp này.

# 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 

cho đầu vào

he #{foo} he 

bạn nhận được đầu ra

"he 3 he" 

Các đầu vào

"he #{bad} he\n" 

sẽ nâng cao một ngoại lệ NameError. Và đầu vào

"\"'\u0002\u0003\u007F|+-" 

sẽ tăng và ngoại lệ ArgumentError phàn nàn rằng đầu vào chứa tất cả ký tự phân cách có sẵn.

0

Cũng có thể ném giải pháp của riêng tôi vào hỗn hợp.

irb(main):001:0> str = '#{13*3} Music' 
=> "\#{13*3} Music" 
irb(main):002:0> str.gsub(/\#\{(.*?)\}/) { |match| eval($1) } 
=> "39 Music" 

Điểm yếu là biểu thức bạn muốn đánh giá có thể có thêm {} trong đó, vì vậy có thể cải thiện regex.

Các vấn đề liên quan