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}
質問
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.
解決
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}
他のヒント
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