I'm trying to find a simple way of editing each line in a file, and I'm having some trouble understanding how to use the File
class to do so.
The file I want to edit has several hundred lines with comma separated values in each line. I'm only interested i开发者_如何学编程n the first value in each line, and I want to delete all values after the first one. I tried to do the following:
File.open('filename.txt', 'r+') do |file|
file.each_line { |line| line = line.split(",")[0] }
file.write
file.close
end
Which doesn't work because File.write
method requires the contents to be written as an argument.
Could someone enlighten me as to how I could achieve the desired effect?
The one of the better solutions(and safest) is to create a temporary file using TempFile, and move it to the original location(using FileUtils) once you are done:
require 'fileutils'
require 'tempfile'
t_file = Tempfile.new('filename_temp.txt')
File.open("filename.txt", 'r') do |f|
f.each_line{|line| t_file.puts line.split(",")[0].to_s }
end
t_file.close
FileUtils.mv(t_file.path, "filename.txt")
Another way to modify the file inplace is to use the -i
switch
ruby -F"," -i.bak -ane 'puts $F[0]' file
File processing using code differs substantially from what we are doing when we, for example, edit the file in a text editor. File operations offered by operating systems are quite limited in that matter (due to numerous, partly historical reasons - think magnetic tapes).
In short, you should probably create another file and write data to it (Mike provided code for that), or load entire file in memory (which can be bad idea if your file is huge) and overwrite it with processed data.
Just for practice, here's how you could actually edit file in-place. As you can see, not the prettiest sight:
File.open('foo', 'r+') do |file|
write_pos = 0
file.each do |line|
word = line.chomp.split(',').first
read_pos = file.pos
file.pos = write_pos
file.puts word
write_pos = file.pos
file.pos = read_pos
end
file.truncate write_pos
end
I think you misunderstand what this line
file.each_line { |line| line = line.split(",")[0].to_s }
really does. It takes a line, splits it on a comma, takes the first value, turns it to a string (which it was already), assigns the result to the block-local variable 'line'. And then?
It goes on to the next line, and nothing is done with the previous one - it's all gone. See the other answers how to remedy this.
The problem with the accepted answer is that it modifies file permissions and ownership (pay attention to that).
Another approach is to use inplace editing inside Ruby (not from the command line):
#!/usr/bin/ruby
def inplace_edit(file, bak, &block)
old_stdout = $stdout
argf = ARGF.clone
argf.argv.replace [file]
argf.inplace_mode = bak
argf.each_line do |line|
yield line
end
argf.close
$stdout = old_stdout
end
inplace_edit 'test.txt', '.bak' do |line|
line = line.gsub(/search1/,"replace1")
line = line.gsub(/search2/,"replace2")
print line unless line.match(/something/)
end
If you don't want to create a backup then change '.bak' to ''.
精彩评论