JasonBorne
JasonBorne

Reputation: 97

Replace line in text file with a new line

I want to replace a line in a file with a new line, example being:

File:

test
test
test
testing
test

Source:

def remove_line(line)
  if line == line
    #remove line including whitespace
    File.open('test.txt', 'a+') { |s| s.puts('removed successfully') }
  end
end

So the expected output of this would be something like this:

remove_line('testing')
test
test
test
removed successfully
test

Now I've done some research and have only been able to find adding a blank line, I guess I could run through it and remove all blank lines and just append to the file, but there has to be an easier way to replace a line with another string?

Upvotes: 4

Views: 4440

Answers (4)

13aal
13aal

Reputation: 1674

I found a reference answer here it's not exactly the same because they're searching for a pattern.

So here's how I would go about doing this:

def remove_line(remove)
  file.open('test.txt', 'a+').each_line do |line|
    line.gsub(remove, '<new/line/here>')
  end
end

This should replace that line given as an argument with whatever new line you want it replaced to..

Upvotes: 1

r3cha
r3cha

Reputation: 111

I've wired solution but here is:

create a function that have file path argument and block with line where we modify each line as we need

def override_file_line(file_path)
  file_lines = File.read(file_path).split("\n")

  updated_lines = []
  file_lines.map do |line|
    updated_lines << yield(line)
  end

  File.open(file_path, 'w') { |line| line.puts updated_lines.join("\n") }
end

Then I call this function and modify line as I need inside block

# here is I add 2 in end of each line
override_file_line(@source_files[7]) do |line|
  "#{line}2"
end

Upvotes: 0

Michael Gaskill
Michael Gaskill

Reputation: 8042

Given this input file, stored as "test.txt":

test
test
test
testing
test

and this sample code:

def remove_line(line_to_remove)
  File.foreach("test.txt").with_index do |line, line_num|
    line.gsub!(/[\r\n]+$/, '')
    if line == line_to_remove
      puts "removed successfully"
    else
      puts line
    end
  end
end

You can successfully run:

remove_line('testing')

and get this output:

test
test
test
removed successfully
test

The function takes the content of the line to remove, and opens the file in "line-by-line" fashion. This is idiomatic Ruby, and should be preferred over "slurping" files, as explained in the accepted answer to this SO question.

Once we have a line, it's necessary to strip the line endings from it. Since we don't know exactly which platform this will be run on (or the text file was created on), we use a regular expression to find all known line ending characters ('\r\n' is Windows, '\n' is Linux/Unix/Mac OS X, and '\r' is Mac Classic).

Then we check to see if the line matches what we want to remove. If it does match, we omit it from the output and instead print that it's been "removed successfully"; otherwise, we output the non-matching line.

This matches the original design intent, however, there are plenty of things to do to improve the design and make an overall more useful function. So why not go ahead and do that?

First, we'll make the function take a filename as an argument. This will remove the hardcoded "test.txt" filename from the foreach call. That will make this change happen:

def remove_line(filename, line_to_remove)
  File.foreach(filename).with_index do |line, line_num|
    line.gsub!(/[\r\n]+$/, '')
    if line == line_to_remove
      puts "removed successfully"
    else
      puts line
    end
  end
end

You can successfully run it this way, and it will produce the exact same output:

remove_line("test.txt", "testing")

Next, let's change how the output occurs, and we'll use a block to do this, because that's The Ruby Way. Here's what the function looks like with a block to output:

def remove_line(filename, line_to_remove, &block)
  proc = block_given? ? block : lambda {|s| puts s }
  File.foreach(filename).with_index do |line, line_num|
    line.gsub!(/[\r\n]+$/, '')
    if line == line_to_remove
      proc.call "removed successfully"
    else
      proc.call line
    end
  end
end

This is built with an optional block argument, so you can call it exactly like the previous version to make it work exactly the same way, or you can call it with an explicit block and do something cool. This example of running it modifies the string slightly before calling puts to print the line:

remove_line("test.txt", "testing") {|s| puts "#{s} in your pants" }

And with that extra pinch of childishness, you get this output:

test in your pants
test in your pants
test in your pants
removed successfully in your pants
test in your pants

You now have the power to do interesting things, built up from other interesting things. Use it wisely and keep doing it The Ruby Way.

Upvotes: 1

user3097405
user3097405

Reputation: 823

First, open the file and save the actual content. Then, replace the string and write the full content back to file.

def remove_line(string)
  # save the content of the file
  file = File.read('test.txt')
  # replace (globally) the search string with the new string
  new_content = file.gsub(string, 'removed succesfully')
  # open the file again and write the new content to it
  File.open('test.txt', 'w') { |line| line.puts new_content }
end

Or, instead of replacing globally:

def remove_line(string)
  file = File.read('test.txt')
  new_content = file.split("\n")
  new_content = new_content.map { |word| word == string ? 'removed succesfully' : word }.join("\n")
  File.open('test.txt', 'w') { |line| line.puts new_content }
end

Upvotes: 6

Related Questions