Ruby - Open File, Find and Replace Multiple Lines

Ruby - Open file, find and replace multiple lines

Change the third line to

replace = replace.gsub(/bbb/, "Replace bbb with 222")

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

Disclaimer: This approach is a naive illustration of Ruby's capabilities, and not a production-grade solution for replacing strings in files. It's prone to various failure scenarios, such as data loss in case of a crash, interrupt, or disk being full. This code is not fit for anything beyond a quick one-off script where all the data is backed up. For that reason, do NOT copy this code into your programs.

Here's a quick short way to do it.

file_names = ['foo.txt', 'bar.txt']

file_names.each do |file_name|
text = File.read(file_name)
new_contents = text.gsub(/search_regexp/, "replacement string")

# To merely print the contents of the file, use:
puts new_contents

# To write changes to the file, use:
File.open(file_name, "w") {|file| file.puts new_contents }
end

Replace line in text file with a new line

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

Search and replace multiple words in file via Ruby

Your loop was kind of inside-out ... do this instead ...

  f.each_line do |line| 
_patterns.each_slice 2 do |a, b|
line.sub! a, b
end
g.puts line
end

Ruby: How to replace text in a file?

There is no possibility to modify a file content in one step (at least none I know, when the file size would change).
You have to read the file and store the modified text in another file.

replace="100"
infile = "xmlfile_in"
outfile = "xmlfile_out"
File.open(outfile, 'w') do |out|
out << File.open(infile).read.gsub(/<appId>\d+<\/appId>/, "<appId>#{replace}</appId>")
end

Or you read the file content to memory and afterwords you overwrite the file with the modified content:

replace="100"
filename = "xmlfile_in"
outdata = File.read(filename).gsub(/<appId>\d+<\/appId>/, "<appId>#{replace}</appId>")

File.open(filename, 'w') do |out|
out << outdata
end

(Hope it works, the code is not tested)

Delete first two lines and add two lines to file

I'd start with something like this:

NEWLINES = {
0 => "New Title",
1 => "\nfff"
}

File.open('test.txt.new', 'w') do |fo|
File.foreach('test.txt').with_index do |li, ln|
fo.puts (NEWLINES[ln] || li)
end
end

Here's the contents of test.txt.new after running:

New Title

fff
aaa
bbb
ccc

The idea is to provide a list of replacement lines in the NEWLINES hash. As each line is read from the original file the line number is checked in the hash, and if the line exists then the corresponding value is used, otherwise the original line is used.

If you want to read the entire file then substitute, it reduces the code a little, but the code will have scalability issues:

NEWLINES = [
"New Title",
"",
"fff"
]

file = File.readlines('test.txt')
File.open('test.txt.new', 'w') do |fo|
fo.puts NEWLINES
fo.puts file[(NEWLINES.size - 1) .. -1]
end

It's not very smart but it'll work for simple replacements.

If you really want to do it right, learn how diff works, create a diff file, then let it do the heavy lifting, as it's designed for this sort of task, runs extremely fast, and is used millions of times every day on *nix systems around the world.

Replace whole line with sub-string in a text file - Ruby

Here is my attempt :

file.txt

First line
Second line
foo
bar
baz foo
Last line

test.rb

f = File.open("file.txt", "r")

a = f.map do |l|
(l.include? 'foo') ? "replacing string\n" : l # Please note the double quotes
end

p a.join('')

Output

$ ruby test.rb 
"First line\nSecond line\nreplacing string\nbar\nreplacing string\nLast line"

I commented # Please note the double quotes because single quotes will escape the \n (that will become \\n). Also, you might want to think about the last line of your file since it will add \n at the end of the last line when there will not have one at the end of your original file. If you don't want that you could make something like :

f = File.open("file.txt", "r")

a = f.map do |l|
(l.include? 'foo') ? "replacing string\n" : l
end

a[-1] = a[-1][0..-2] if a[-1] == "replacing string\n"

p a.join('')

How to delete lines from multiple files

There are a number of things wrong with your code, and you're not safely handling your file changes.

Meditate on this untested code:

ACCESS_FILES = Dir.glob("D:/new_work/*-access.txt")

File.foreach('D:/mywork/list.txt') do |target|
target = target.strip.sub(/,$/, '')

ACCESS_FILES.each do |filename|
new_filename = "#{filename}.new"
old_filename = "#{filename}.old"

File.open(new_filename, 'w') do |fileout|
File.foreach(filename) do |line_in|
fileout.puts line_in unless line_in[target]
end
end

File.rename(filename, old_filename)
File.rename(new_filename, filename)
File.delete(old_filename)
end
end
  • In your code you use:

    File.open('D:\\mywork\\list.txt').read

    instead, a shorter, and more concise and clear way would be to use:

    File.read('D:/mywork/list.txt')

    Ruby will automatically adjust the pathname separators based on the OS so always use forward slashes for readability. From the IO documentation:

Ruby will convert pathnames between different operating system conventions if possible. For instance, on a Windows system the filename "/gumby/ruby/test.rb" will be opened as "\gumby\ruby\test.rb".

The problem using read is it isn't scalable. Imagine if you were doing this in a long term production system and your input file had grown into the TB range. You'd halt the processing on your system until the file could be read. Don't do that.

Instead use foreach to read line-by-line. See "Why is "slurping" a file not a good practice?". That'll remove the need for

    value.gsub!(/\r\n?/, "\n")
value.each_line do |line|
line.chomp!
  • While

    Dir.glob("D:/new_work/*-access.txt") do |fn|

    is fine, its placement isn't. You're doing it for every line processed in your file being read, wasting CPU. Read it first and store the value, then iterate over that value repeatedly.

  • Again,

    text = File.read(fn)

    has scalability issues. Using foreach is a better solution. Again.

  • Replacing the text using gsub is fast, but it doesn't outweigh the potential problems of scalability when line-by-line IO is just as fast and sidesteps the issue completely:

    replace = text.gsub(line.strip, "")
  • Opening and writing to the same file as you were reading is an accident waiting to happen in a production environment:

    File.open(fn, "w") { |file| file.puts replace }

    A better practice is to write to a separate, new, file, rename the old file to something safe, then rename the new file to the old file's name. This preserves the old file in case the code or machine crashes mid-save. Then, when that's finished it's safe to remove the old file. See "How to search file text for a pattern and replace it with a given value" for more information.

A final recommendation is to strip all the trailing commas from your input file. They're not accomplishing anything and are only making you do extra work to process the file.



Related Topics



Leave a reply



Submit