How to Modify a Text File in Ruby

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

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)

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

Replace a word in a file in Ruby

You'll also need to write back to the file. File.open without any arguments opens the file for reading. You can try this:

# load the file as a string
data = File.read("hello.txt")
# globally substitute "install" for "latest"
filtered_data = data.gsub("install", "latest")
# open the file for writing
File.open("hello.txt", "w") do |f|
f.write(filtered_data)
end

Edit each line in a file in Ruby

The one of the better solutions(and safest) is to create a temporary file using TempFile, and move it to the original location(using FileUtils) once you are done:

   require 'fileutils'
require 'tempfile'

t_file = Tempfile.new('filename_temp.txt')
File.open("filename.txt", 'r') do |f|
f.each_line{|line| t_file.puts line.split(",")[0].to_s }
end
t_file.close
FileUtils.mv(t_file.path, "filename.txt")

Replace a line from a text file

Let's analyze what's going wrong here. File#any? returns true or false, according as its block returns true for at least one of the block arguments passed. You are not returning true or false from the block and you have no interest in the question of whether the result of File#any? is true or false. So this is clearly the wrong method.

Moreover, as you rightly say, you are writing a new (added) line, not in any way replacing the existing line.

What you want to do is cycle through the file line by line, reading each line, and:

  • If the line doesn't contain the target string, write the very same line

  • If the line does contain the target string, write the substituted line

Thus you will read the whole file and write the whole file (with or without the substitution) in its place. This approach actually involves two files, since you have already read the line before the time comes to write it.

Alternatively you could just read the whole file at once, split it into lines, look for the target and perform the substitution, join back into a single string, and write it. If the files are small (so that the strings are small), this is much the simplest way.

For the correct patterns, see https://stackoverflow.com/a/4399299/341994

Replace a string in txt file with Ruby

I think that @ShaneQful's response is good because it uses your code but you can as he stated make this far easier with

file_name = "D:/test.txt"
old_color = "white"
new_color = "black"
File.write(file_name,File.open(file_name,&:read).gsub(old_color,new_color))

What this does is it opens file_name reads it out into a string. Replaces (#gsub) all the instances of old_color with new_color and then writes it back to file_name.

Simple, easy, clean and concise.

Update

Benchmarking of File#read, File.open(file_name,&:read), and File.open with block read into a string and then written back to file_name(as in ShaneQful's example)

This was benchmarked against Jack London's White Fang which contains ~75,000 words and 645 instances of the word white

#Benchmark
Rehearsal --------------------------------------------------------
File#read 0.375000 0.484000 0.859000 ( 1.462000)
File.open(&:read) 0.437000 0.530000 0.967000 ( 1.480000)
File.open with block 1.404000 0.359000 1.763000 ( 2.150000)
----------------------------------------------- total: 3.589000sec

user system total real
File#read 0.452000 0.499000 0.951000 ( 1.401000)
File.open(&:read) 0.483000 0.421000 0.904000 ( 1.445000)
File.open with block 1.529000 0.328000 1.857000 ( 2.120000)
#Fruity
Running each test 2 times. Test will take about 3 minutes.
File.open(&:read) is similar to File#read
File#read is faster than File.open with block by 50.0% ± 10.0%

It seems File#read and File.open(file_name,&:read) trade hands back and forth as to the speed of implementation but utilizing a true block to handle the same operation is always much slower for this type of thing.

Synopsis for easy procedures like this use read or #open(file_name,&:read) (Symbol#to_proc). If you need to perform elaborate changes that may take multiple lines or conditional options then I would use a block

Zip::ZipFile: How to modify contents of inner textfiles without unpacking zip?

So with the help of Ben, here's one solution:

require "rubygems"
require "zip/zip"
zip_file_name = "src/test.zip"

Zip::ZipFile.open(zip_file_name) do |zipfile|
files = zipfile.select(&:file?)
files.each do |zip_entry|
contents = zipfile.read(zip_entry.name)
zipfile.get_output_stream(zip_entry.name){ |f| f.puts contents + ' added some text' }
end
zipfile.commit
end

I though I had tried this before - anyways. Thanks a lot!



Related Topics



Leave a reply



Submit