Question

I am copying each line of a file to separate files, depending on the content. Each line begins with "foo" or "bar", and I want to read the first few characters of each line and dynamically change the file name variable.

readfile = File.open("myfile.txt", 'r')
file_foo = File.open("file1.txt", 'w')
file_bar = File.open("file2.txt", 'w')

for line in readfile
  writefile = 'file_' + line[0..2]
  writefile.write(line)
end
file_foo.close
file_bar.close

This throws an error, as the variable writefile refers to the string "file_foo" or "file_bar".

Suggestions for an elegant Rubyist solution? I couldn't see from the documentation how send method could be applied here if that is indeed the way to go.

Était-ce utile?

La solution

Make a hash of files:

readfile = File.open("myfile.txt", 'r')

files = {
  'foo' => File.open("file1.txt", 'w'),
  'bar' => File.open("file2.txt", 'w')
}

for line in readfile
  files[line[0..2]].write(line)
end
files.each {|k, v| v.close}

Autres conseils

I think you are looking for eval. It will take a string and evaluate it as Ruby code in the current context. So your example becomes:

readfile = File.open("myfile.txt", 'r')

file_foo = File.open("file1.txt", 'w')
file_bar = File.open("file2.txt", 'w')

for line in readfile
  eval('file_' + line[0..2]).write(line)
end

filefoo.close
filebar.close

However, you asked for a "Rubyist" approach. Using eval is certainly NOT a Rubyist approach. Nor is the use of for loops. I'll take a crack at a more Rubyist approach:

infile  = "myfile.txt"
foofile = "file1.txt"
barfile = "file2.txt"

def append_to_file(path, content)
  File.open(path, 'a') { |f| f << content }
end

IO.readlines(readfile).each do |line|
  case line
  when /^foo/
    append_to_file(foofile, line)
  when /^bar/
    append_to_file(barfile, line)
  end
end

You cannot use send because what you are trying to convert a string into is not a method but is a local variable.

From Ruby 2.1, you will be able to use Binding#local_variable_get.

for line in readfile
  writefile = binding.local_variable_get(:"file_#{line[0..2]}")
  writefile.write(line)
end
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top