1

Ok, I have this ruby script which opens a file and passes each line to the line_parser method. This method simply changes tabs for a couple of spaces. Here it is:

$lines = []

def line_parser(line)   
    line.gsub! /\t/, '  '
    $lines[$lines.length] = line
end

f = File.open(ARGV[0], 'r').each { |line| line_parser(line) }
f.close

f = File.open(ARGV[0], 'w')
$lines.each { |line| f.puts line}
f.close

As you can see it has two loops; one loop to iterate over the lines of text inside the file an place them inside of an array. And the other loop writes the file all over again with the new lines from the recently created array.

My question is simple I think: How can I refactor this snippet so that I do all the steps described above inside one loop only?

I know it can be done, I just have not been able to do it with my current ruby knowledge.

Thanks in advance!

2
  • 2
    This question gives an alternative: stackoverflow.com/questions/5452781/…, which is to write a temp file then overwrite the input file, rather than looping through the lines again. Although if you simply looking for a simple way to do this (not just a better ruby way), i believe sed -i 's/\t/ /g' yourfile would do the trick. Commented Aug 28, 2011 at 21:10
  • this kind of question would be better off at codereview.stackexchange.com Commented Aug 28, 2011 at 21:48

3 Answers 3

2
out = ""
File.open(ARGV[0], "r+") do |f|
  f.each do |line|
    out << line.gsub(/\t/, '  ')
  end
  f.pos=0
  f.print out
end

This just iterates over the file once but it still has to cache the file in a string before finally writing it to the file. If you want to avoid caching you will have to write to a temporary file first. Once done reading/writing you would simply delete the old file and replace it with the new temporary file by renaming it.

Sign up to request clarification or add additional context in comments.

1 Comment

Your code works pretty well, a million times better than what I did. :) However, I'm just wondering.. Could this be done w/o temp. Files or Strings or Arrays at all? (just wondering..)
2
#!/usr/local/bin/ruby -w

src = File.read(ARGV[0])

File.open(ARGV[0], 'w') { | io |
  src.each_line { | line |
    io << line.gsub(/\t/, '  ')
  }
}

Please note that this is actually cheated. Reading the whole file is in fact a loop. But since you are reading and then writing to the same file, I doubt you can avoid having 2 loops (one for reading and one for writing).

2 Comments

Looks good, very Idiomatic. Could you explain the io parameter for the block of the File.open ? Thank you very much for your answer !
@user766388: It's the Ruby way to express io = File.open(...); io << ...; io.close.
1

Can we use rubygems?

require 'rubygems'
require 'facets/file'

# Version 1
File.write('outfile', File.read('infile').tr("\t", ' '))

# Version 2
File.rewrite('inoutfile') { |str| str.tr("\t", ' ') }

3 Comments

I could use anything I guess. that seems like an interesting solution. What packages am I actually pulling to the file when I do: require 'rubygems' ? pardon my ignorance. Thanks
@user - Rubygems is the de-facto package management system for ruby. When you require rubygems you're simply loading or activating the package management system. After this is done individual packages can then be activated with require, like I do here with the facets package. To learn more and search or browse available packages go here: rubygems.org
That was incredibly useful to me. Nicely explained, thank you very much! I really need these little insights into the language now.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.