8

Suppose, I have an input.txt file with the following text:

First line
Second line
Third line
Fourth line

I want to delete, for example, the second and fourth lines to get this:

First line
Third line

So far, I've managed to delete only one the second line using this code

require 'fileutils'

File.open('output.txt', 'w') do |out_file|
  File.foreach('input.txt') do |line|
     out_file.puts line unless line =~ /Second/
  end
end

FileUtils.mv('output.txt', 'input.txt')

What is the right way to delete multiple lines in text file in Ruby?

Alex Nikolsky
  • 2,087
  • 6
  • 21
  • 36
  • What are you trying to do ? – Pedro Lobito May 29 '16 at 23:00
  • The code you posted doesn't delete _any_ of the lines you show in your sample input file. I think we need a clearer description of what criteria you want to use to delete lines. Do you want to delete every even line? Do you want to delete lines having any of a number of specific strings? We can't tell from what you've posted. – Wayne Conrad May 30 '16 at 04:46
  • @WayneConrad I would like to know either how to delete lines containing particular string or just lines with specified index – Alex Nikolsky May 30 '16 at 05:46

2 Answers2

8

Deleting lines cleanly and efficiently from a text file is "difficult" in the general case, but can be simple if you can constrain the problem somewhat.

Here are some questions from SO that have asked a similar question:

There are numerous others, as well.

In your case, if your input file is relatively small, you can easily afford to use the approach that you're using. Really, the only thing that would need to change to meet your criteria is to modify your input file loop and condition to this:

File.open('output.txt', 'w') do |out_file|
  File.foreach('input.txt').with_index do |line,line_number|
     out_file.puts line if line_number.even?  # <== line numbers start at 0
  end
end

The changes are to capture the line number, using the with_index method, which can be used due to the fact that File#foreach returns an Enumerator when called without a block; the block now applies to with_index, and gains the line number as a second block argument. Simply using the line number in your comparison gives you the criteria that you specified.

This approach will scale, even for somewhat large files, whereas solutions that read the entire file into memory have a fairly low upper limit on file size. With this solution, you're more constrained by available disk space and speed at which you can read/write the file; for instance, doing this to space-limited online storage may not work as well as you'd like. Writing to local disk or thumb drive, assuming that you have space available, should be no problem at all.

Community
  • 1
  • 1
Michael Gaskill
  • 7,913
  • 10
  • 38
  • 43
5

Use File.readlines to get an array of the lines in your input file.

input_lines = File.readlines('input.txt')

Then select only those with an even index.

output_lines = input_lines.select.with_index { |_, i| i.even? }

Finally, write those in your output file.

File.open('output.txt', 'w') do |f|
  output_lines.each do |line|
    f.write line
  end
end
floum
  • 1,149
  • 6
  • 17
  • In case anyone needs it, I just changed the second step with `input_lines.pop(number_of_lines)` to remove lines from the end of the file or with `input_lines.shift(number_of_lines)` to remove lines from the beginning of the file and it works like a charm. – alexventuraio Jan 16 '22 at 06:14