4

Ruby beginner here!

I am aware that Ruby's File.open method has certain modes like r,w,a,r+,w+,a+ and the complimentary b. I totally understand the use of r,w and a modes. But I cannot seem to understand how to use the ones with the '+' symbol. Can anyone provide me with some links where there are examples as well as explanations for the use of it?

Can it be used to read a line and edit/replace it in place by a equal amount of content? If so, then how?

Sample data file: a.txt

aaa
bbb
ccc
ddd

Demo.rb

file = File.open "a.txt","r+"
file.each do |line|
  line = line.chomp
  if(line=="bbb")then
  file.puts "big"
  end
end
file.close

I am trying to replace "bbb" with "big" but I am getting this:- in notepad++

aaa
bbb
big

ddd

in notepad

aaa
bbb
bigddd
Jazz
  • 639
  • 1
  • 11
  • 28

2 Answers2

11

snatched this documentation from another answer, so not mine, the solution is mine

r  Read-only mode. The file pointer is placed at the beginning of the file. This is the default mode. 
r+ Read-write mode. The file pointer will be at the beginning of the file. 
w  Write-only mode. Overwrites the file if the file exists. If the file does not exist, creates a new file for writing. 
w+ Read-write mode. Overwrites the existing file if the file exists. If the file does not exist, creates a new file for reading and writing.
a  Write-only mode. The file pointer is at the end of the file if the file exists. That is, the file is in the append mode. If the file does not exist, it creates a new file for writing. 
a+ Read and write mode. The file pointer is at the end of the file if the file exists. The file opens in the append mode. If the file does not exist, it creates a new file for reading and writing.

EDIT: here the solution to your sample, most of the time the whole string is gsubbed and written back to the file but 'infile' replacing without rewriting the whole file is also possible You should be cautious to replace with a string of the same length.

File.open('a.txt', 'r+') do |file|
  file.each_line do |line|
    if (line=~/bbb/)
      file.seek(-line.length-3, IO::SEEK_CUR)
      file.write 'big'
    end
  end
end 

=>
aaa
big
ccc
ddd

And this is a more conventional way, though more concise then most other solutions

File.open(filename = "a.txt", "r+") { |file| file << File.read(filename).gsub(/bbb/,"big") } 

EDIT2: i now realize this can still shorter

File.write(f = "a.txt", File.read(f).gsub(/bbb/,"big"))
peter
  • 41,770
  • 5
  • 64
  • 108
  • Any examples for their(w+,r+) usage? – Jazz Apr 16 '12 at 11:45
  • So you are reading an entire file into a variable, then performing the substitution, and the writing the variable's contents back to the file. Am I right? I was looking for something kinda inline. – Jazz Apr 16 '12 at 12:42
  • Why copypaste what's already in every documenation (file modes) and several similar answers here? – Ernest Apr 16 '12 at 13:48
  • it's sad to view your answer downvoted because you repeat some documentation while it's the solution where it's all about, OP asks to not replace and rewrite the whole of the file, just part of it and that's exactly what this does.. – peter Apr 16 '12 at 15:53
  • Actually, after your edit, I must admit that this is a good answer. Still don't like the copypaste documentation though, but +1. – Ernest Apr 16 '12 at 15:58
  • Sorry for the trouble! On my system the output is still not proper (but closer than before). aaabigbb\r\n ccc\r\n ddd\r\n – Jazz Apr 17 '12 at 04:27
  • 1
    Jazz, cause you probably have extra whitespace, line or return characters somewhere, anyway, i would use this method only to do operations on very big files so that they don't have to be copied, i added a more common solution where the file is rewritten entirely but without making use of a second file. Hope this is good enough for you – peter Apr 17 '12 at 10:07
  • TA DA! It worked, Peter, your code worked. I just had to change the first argument to file.seek as '-line.length-1'. I have got to do a lot of reading to understand the seek method. But THANKS! And yes your are right while doing the 'seek' I have to consider the character of CR and LF too, and not just LF. A big THANK YOU! – Jazz Apr 17 '12 at 13:05
0

So you are reading an entire file into a variable, then performing the substitution, and the writing the variable's contents back to the file. Am I right? I was looking for something kinda inline

That's the way to do it. You can alternativly use IO#readlines to read all lines into Array and then process them.

And this has been already answered:

How to search file text for a pattern and replace it with a given value

If you are woried about performance or memory usage then use the right tools for the right job. On *nix (or cygwin on windows):

sed -i -e "s/bbb/big/g" a.txt

Will do exactly what you want.

Community
  • 1
  • 1
Ernest
  • 8,701
  • 5
  • 40
  • 51
  • you would seriously install cygwin to do a string replace on windows ? Ruby itself is fast and able enough and if you would need an external tool, there are plenty of windows tools to choose from – peter Apr 16 '12 at 15:32
  • @peter where do I imply that you need install cygwin to this task? A lot of people just have it already. As for windows tools, probably true, and you are encouraged to give an example. – Ernest Apr 16 '12 at 15:36
  • you are a nix user i suppose ? 8>). An example: there is a sed.exe in the ruby\bin map that you could use – peter Apr 16 '12 at 15:48
  • Ernest: question, how you put the @Ernest in the beginning of a comment, doesn't work with me – peter Apr 16 '12 at 15:49
  • Cannot use 'sed' as I am working on windows. And YES, I do not want to create a new file, just want to change some contents in an existing one. – Jazz Apr 17 '12 at 04:42
  • Jazz, you have SED, see my third comment here, i don't advise using it though, the last solution from my own answer is probably the best for you – peter Apr 17 '12 at 10:11
  • Ernest, Thanks You in an earnest way for your effort! And Peter, in my Ruby (1.9.3) /bin folder there is no sed.exe file. Anyways Problem SOLVED. – Jazz Apr 17 '12 at 13:10