0

I am new to ruby. I am trying to save a change within same text file using Ruby. How do I approach this?

This is what I tried:

f = File.open("D:/test.txt", "r")
oldcolor = "white"
newcolor = "black"
f.each_line do |line|
  line.sub(oldcolor,newcolor)
  puts line
end
f.close

Any suggestions how to use variables in sub instead of regex or may be any other method to replace "white" to "black" ?

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
The Third
  • 785
  • 2
  • 10
  • 30

3 Answers3

1

What you need to do is :

f = File.open("D:/test.txt", "r")
oldcolor = "white"
newcolor = "black"
newfile_str = ""
f.each_line do |line|
  newfile_str += line.sub(oldcolor,newcolor)
end
f.close
File.open("D:/test.txt", "w") {|file| file.puts newfile_str}

There are easier ways of doing this but I wanted to use your own code to make it easier for you to understand.

Explanation: I'm storing the modified lines in a string and then writing it to the file.

Community
  • 1
  • 1
ShaneQful
  • 2,140
  • 1
  • 16
  • 22
  • Don't aggregate the lines to be written into a string. That creates a potential problem if the program runs out of memory. Instead of storing the strings, immediately write them out. That keeps as much available memory and doesn't impact I/O speed. http://stackoverflow.com/a/25189286/128421 – the Tin Man Nov 26 '14 at 18:44
1

I think that @ShaneQful's response is good because it uses your code but you can as he stated make this far easier with

file_name = "D:/test.txt"
old_color = "white"
new_color = "black"
File.write(file_name,File.open(file_name,&:read).gsub(old_color,new_color))

What this does is it opens file_name reads it out into a string. Replaces (#gsub) all the instances of old_color with new_color and then writes it back to file_name.

Simple, easy, clean and concise.

Update

Benchmarking of File#read, File.open(file_name,&:read), and File.open with block read into a string and then written back to file_name(as in ShaneQful's example)

This was benchmarked against Jack London's White Fang which contains ~75,000 words and 645 instances of the word white

#Benchmark
Rehearsal --------------------------------------------------------
File#read              0.375000   0.484000   0.859000 (  1.462000)
File.open(&:read)      0.437000   0.530000   0.967000 (  1.480000)
File.open with block   1.404000   0.359000   1.763000 (  2.150000)
----------------------------------------------- total: 3.589000sec

                           user     system      total        real
File#read              0.452000   0.499000   0.951000 (  1.401000)
File.open(&:read)      0.483000   0.421000   0.904000 (  1.445000)
File.open with block   1.529000   0.328000   1.857000 (  2.120000)
#Fruity
Running each test 2 times. Test will take about 3 minutes.
File.open(&:read) is similar to File#read
File#read is faster than File.open with block by 50.0% ± 10.0%

It seems File#read and File.open(file_name,&:read) trade hands back and forth as to the speed of implementation but utilizing a true block to handle the same operation is always much slower for this type of thing.

Synopsis for easy procedures like this use read or #open(file_name,&:read) (Symbol#to_proc). If you need to perform elaborate changes that may take multiple lines or conditional options then I would use a block

engineersmnky
  • 25,495
  • 2
  • 36
  • 52
  • **I am new to ruby.** And you used &:read? I'm old to ruby, and I wouldn't write that perlish code. There are also other advantages to using blocks when opening files. – 7stud Sep 02 '14 at 21:16
  • @7stud I chose to use Symbol#to_proc because the implementation is cleaner and has better performance than the block in this case. Please remember the OP also stated "...or may be any other method to replace 'white' to 'black' ?" – engineersmnky Sep 03 '14 at 01:45
  • Is there any advantage to `File.open(name,&:read)` over `File.read(name)` ? – Frederick Cheung Sep 03 '14 at 07:10
  • @FrederickCheung not sure. I will benchmark all three later on today and post results. – engineersmnky Sep 03 '14 at 11:33
  • @FrederickCheung benchmarking added. It seems `File#read` vs `File.open(file_name, &:read)` have almost no difference and they trade back and forth as to which method is faster. – engineersmnky Sep 03 '14 at 16:15
  • @7stud I have added benchmarking to show why I chose not to use a block for such a simple procedure. – engineersmnky Sep 03 '14 at 16:22
  • Using `read` makes the code unscalable as the script will crawl when memory is full. Instead, use line-by-line I/O which remains constant and is scalable. http://stackoverflow.com/a/25189286/128421 – the Tin Man Nov 26 '14 at 18:42
1

I am new to ruby. I am trying to save a change within same text file using ruby.

For all practical purposes, you can't, and you really don't want to anyway--ever. Instead, write to a new file, delete the old file, rename the new file to old file name.

7stud
  • 46,922
  • 14
  • 101
  • 127