Modifying content in a file

G

Ghislain Mary

Hi all,

I've written a little code that replaces some content in a file, but I
really think there is a better way to do so. Here's my code:

def clean_file(filename)
# Define the new caption
caption = "caption: new caption"

# Process the content of the file
File.open(filename) do |from_file|
File.open(filename + ".new", 'w') do |to_file|
from_file.each do |line|
case
when line =~ /^caption:.*$/: line.sub!($&, caption)
end
to_file.print line
end
end
end

# Rename the file
File.delete(filename)
File.rename(filename + ".new", filename)
end

Can't it be done using the read-write mode on the file? Any better
proposition is welcome ;)

Thanks.

Ghislain
 
R

Robert Klemme

Ghislain Mary said:
Hi all,

I've written a little code that replaces some content in a file, but I
really think there is a better way to do so. Here's my code:

def clean_file(filename)
# Define the new caption
caption = "caption: new caption"

# Process the content of the file
File.open(filename) do |from_file|
File.open(filename + ".new", 'w') do |to_file|
from_file.each do |line|
case
when line =~ /^caption:.*$/: line.sub!($&, caption)
end
to_file.print line

You can do that easier:

to_file.puts( /^caption:/ =~ line ? caption : line )
end
end
end

# Rename the file
File.delete(filename)
File.rename(filename + ".new", filename)
end

Can't it be done using the read-write mode on the file? Any better
proposition is welcome ;)

Depends on your file's sizes. If they are moderately sized you can slurp
the whole thing in, modify contents in mem with a single gsub and then
write them in one go.

Regards

robert
 
G

Ghislain Mary

Robert Klemme a écrit :
You can do that easier:

to_file.puts( /^caption:/ =~ line ? caption : line )

In fact, I am doing several substitution, but I forgot to tell it.
Depends on your file's sizes. If they are moderately sized you can slurp
the whole thing in, modify contents in mem with a single gsub and then
write them in one go.

Thanks. Since the file can be quite large, I continue using something
like before but with a Tempfile now. That gives something like:

require 'tempfile'
require 'fileutils'
include FileUtils

def clean_file(filename)
# Define the new caption
caption = "caption: my caption"

# Process the content of the file
begin
tf = Tempfile.new(filename)
File.open(filename) do |from_file|
from_file.each do |line|
case
when line =~ /^caption:/: line.sub!($&, caption)
# Other substitutions...
end
tf.print line
end
end
ensure
tf.close unless tf.nil?
end

# Replace the previous file with the new.
cp(tf.path, filename)
end


But can't we open a file in read-write mode and directly do
substitutions in it without using an auxiliary file?

Ghislain
 
L

Lyndon Samson

But can't we open a file in read-write mode and directly do
substitutions in it without using an auxiliary file?
You can use IO.pos to seek within a file, however what will you do if
your substitute string is not the same length as the original string?
Files have no concept of insert/delete.
 
H

Hal Fulton

Lyndon said:
You can use IO.pos to seek within a file, however what will you do if
your substitute string is not the same length as the original string?
Files have no concept of insert/delete.

Although it would be an interesting exercise to create
a descendant of File that would behave that way. It would
be analogous to the way strings are reallocated in RAM.

Inefficient, certainly, but disk drives are faster than
they were in 1980. ;)


Hal
 
B

Brian Schröder

Robert Klemme a écrit :

In fact, I am doing several substitution, but I forgot to tell it.


Thanks. Since the file can be quite large, I continue using something
like before but with a Tempfile now. That gives something like:

require 'tempfile'
require 'fileutils'
include FileUtils

def clean_file(filename)
# Define the new caption
caption = "caption: my caption"

# Process the content of the file
begin
tf = Tempfile.new(filename)
File.open(filename) do |from_file|
from_file.each do |line|
case
when line =~ /^caption:/: line.sub!($&, caption)
# Other substitutions...
end
tf.print line
end
end
ensure
tf.close unless tf.nil?
end

# Replace the previous file with the new.
cp(tf.path, filename)
end

But can't we open a file in read-write mode and directly do
substitutions in it without using an auxiliary file?

You can do this, but only if you don't want to insert something inside
of the file, you can only overwrite parts of the file. So here is an
example:

File.open('test', 'w') { | f | f.print('This is a test') }
puts File.read('test')

File.open('test', 'r+') { | f | f.print('That') }
puts File.read('test')

File.open('test', 'r+') { | f | f.seek(5); f.print('was') }
puts File.read('test')

Output:
This is a test
That is a test
That wasa test

regards,

Brian
 
G

Ghislain Mary

You can use IO.pos to seek within a file, however what will you do if
Although it would be an interesting exercise to create
a descendant of File that would behave that way. It would
be analogous to the way strings are reallocated in RAM.

Inefficient, certainly, but disk drives are faster than
they were in 1980. ;)

That's it. I know that files don't have concept of insert or delete, but
I was telling myself that maybe Ruby provided a way to abstract this
lack and to provide a tranparent mean to modify the content of a file.
But I can do without it ;)

Thanks all.

Ghislain
 
R

Robert Klemme

Ghislain Mary said:
Robert Klemme a écrit :

In fact, I am doing several substitution, but I forgot to tell it.


Thanks. Since the file can be quite large, I continue using something
like before but with a Tempfile now. That gives something like:

require 'tempfile'
require 'fileutils'
include FileUtils

def clean_file(filename)
# Define the new caption
caption = "caption: my caption"

# Process the content of the file
begin
tf = Tempfile.new(filename)
File.open(filename) do |from_file|
from_file.each do |line|
case
when line =~ /^caption:/: line.sub!($&, caption)
# Other substitutions...
end
tf.print line
end
end
ensure
tf.close unless tf.nil?
end

# Replace the previous file with the new.
cp(tf.path, filename)
end

Another small remark: I'd refactor the actual replacement out of this
method into another method that receives two IO instances. That's more
modular and it makes testing easier (you can use StringIO).
But can't we open a file in read-write mode and directly do
substitutions in it without using an auxiliary file?

Yes, but this has the problems others have pointed out. This works only
good as long as substitutions have the same lenght as the original piece
they replace - a rather seldom scenario I figure.

Kind regards

robert
 
W

William Morgan

Excerpts from Ghislain Mary's mail of 18 Apr 2005 (EDT):
def clean_file(filename)
# Define the new caption
caption = "caption: new caption"

# Process the content of the file
File.open(filename) do |from_file|
File.open(filename + ".new", 'w') do |to_file|
from_file.each do |line|
case
when line =~ /^caption:.*$/: line.sub!($&, caption)
end
to_file.print line
end
end
end

# Rename the file
File.delete(filename)
File.rename(filename + ".new", filename)
end

Sure, here's a two line version:

def clean_file(filename)
caption = "caption: new caption"
system %%ruby -i -pe '$_.gsub! /^caption:.*$/,"#{caption}"' #{filename}%
end

(heh heh heh...)
 
W

William Morgan

Excerpts from William Morgan's mail of 19 Apr 2005 (EDT):
Sure, here's a two line version:

def clean_file(filename)
caption = "caption: new caption"
system %%ruby -i -pe '$_.gsub! /^caption:.*$/,"#{caption}"' #{filename}%
end

Oh wait, I thought of a shorter version:

def clean_file(filename)
caption = "caption: new caption"
system %%perl -i -pe 's/^caption:.*$/#{caption}/' #{filename}%
end
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top