# Using external text file to do search and replace?

**URL:** <https://www.ruby-forum.com/t/using-external-text-file-to-do-search-and-replace/112714>\
**Category:** Ruby\
**Created:** [August 22, 2007, 2:01pm UTC](https://www.ruby-forum.com/t/using-external-text-file-to-do-search-and-replace/112714 "2007-08-22T14:01:55Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![dcgrigsby](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/b5e925/32.png) [@dcgrigsby](https://www.ruby-forum.com/u/dcgrigsby)\
**Post date:** [August 22, 2007, 2:01pm UTC](https://www.ruby-forum.com/t/using-external-text-file-to-do-search-and-replace/112714/1 "2007-08-22T14:01:55Z")

</div>

I recently started to take an interest in Ruby programming and with  
the help of some people and by reading Learn to program by Chris P.  
I made myself a little program that has come to a halt because I don’t  
know if or how I can use an external file where I store the strings I  
want to search and replace in text type files.

This is what I have so far:

txt\_files = Dir.glob(’\*\*/\*.txt’).each do |path|  
puts path  
txts = path.to\_s  
file = File.open(txts).readlines.each { |line|  
if line.match(/PROMOS:/)  
then line.gsub!(/PROMO1=[A-Za-z0-9]+/, ‘PROMO1=some\_text’)  
end }  
file2=File.open(txts, “w”)  
file2.write( file )  
end

What I want is to be able to use an external file where I store the  
values I want to search and replace with “line.gsub!” eg.: “/PROMOn=[A-  
Za-z0-9]+/, ‘PROMOn=some\_other\_text’”.  
I will need to use RubyScript2Exe because I’m not sure that on some  
other machines I will have ruby installed and it’s easier to just put  
the strings I want to search and replace in an external file that is  
located in the same folder as the script or on a predefined path.

Any ideas, hints, improvements and critiques are highly welcome.

---

<div class="post-metadata">

**Author:** ![dcgrigsby](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/b5e925/32.png) [@dcgrigsby](https://www.ruby-forum.com/u/dcgrigsby)\
**Post date:** [August 22, 2007, 4:44pm UTC](https://www.ruby-forum.com/t/using-external-text-file-to-do-search-and-replace/112714/2 "2007-08-22T16:44:34Z")

</div>

Alle mercoledì 22 agosto 2007, Dan G. ha scritto:

> txts = path.to\_s  
> Za-z0-9]+/, ‘PROMOn=some\_other\_text’".  
> I will need to use RubyScript2Exe because I’m not sure that on some  
> other machines I will have ruby installed and it’s easier to just put  
> the strings I want to search and replace in an external file that is  
> located in the same folder as the script or on a predefined path.
> 
> Any ideas, hints, improvements and critiques are highly welcome.

First some comments about your code:

- path is already a string, so calling to\_s on it does nothing.
- You can replace the File.open(txts).readlines part with  
File.readlines(txts), which is (in my opinion) clearer and doesn’t force  
you  
to remember to close the file (which by the way, you don’t do).
- When you write to file2, you can use the block form of File.open,  
which  
takes care of closing the file for you.

Here’s how what I’d have written:

Dir.glob(‘\*\*/\*.txt’).each do |path|  
lines = File.readlines(path)  
if line.match(/PROMOS:/)  
lines.map!{|l| l.gsub(/PROMO1=[A-Za-z0-9]+/, ‘PROMO1=some\_text’)  
end  
File.open(path, ‘w’){|f| f.write lines}  
end

As you can see, I’ve also replaced the each/gsub! combination with  
map!/gsub,  
which, in my opinion makes clearer what you’re doing (changing the  
contents  
of the array).

As for storing the search/replacement pairs on a file, I’d use YAML.  
It’s  
included in the standard library, so there shouldn’t be problems with  
RubyScript2Exe. You can get information on yaml for ruby at  
[http://yaml4r.sourceforge.net/](http://yaml4r.sourceforge.net/) (look in particular at the cookbook and  
doc  
sections). A simple example could be this:

‘PROMO1=[A-za-z0-9]+’: ‘PROMO1=some\_text’  
‘PROMO2=[A-za-z0-9]+’: ‘PROMO1=some\_other\_text’  
…

When read into ruby using YAML.load, this would return the following  
hash:

{  
‘PROMO1=[A-za-z0-9]+’ =\> ‘PROMO1=some\_text’,  
‘PROMO2=[A-za-z0-9]+’ =\> ‘PROMO1=some\_other\_text’  
}

You could then create regexps using Regexp.new. (Actually, you can also  
store  
the regexps directly in the yaml file, prefixing them with the  
string !ruby/regexp, but I think the file is easier to read/write this  
way).

I hope this helps.

Stefano

---

<div class="post-metadata">

**Author:** ![dcgrigsby](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/b5e925/32.png) [@dcgrigsby](https://www.ruby-forum.com/u/dcgrigsby)\
**Post date:** [August 22, 2007, 10:04pm UTC](https://www.ruby-forum.com/t/using-external-text-file-to-do-search-and-replace/112714/3 "2007-08-22T22:04:05Z")

</div>

On Aug 22, 6:55 am, Dan G. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> txts = path.to\_s  
> Za-z0-9]+/, ‘PROMOn=some\_other\_text’".  
> I will need to use RubyScript2Exe because I’m not sure that on some  
> other machines I will have ruby installed and it’s easier to just put  
> the strings I want to search and replace in an external file that is  
> located in the same folder as the script or on a predefined path.
> 
> Any ideas, hints, improvements and critiques are highly welcome.

# First line of file is regular expression; second is

# substitution.

temp = IO.readlines( “substitute” ).map{|s| s.chomp }  
reg\_exp = Regexp.new( temp[0] )  
new\_text = temp[1]

Dir[“\*\*/\*.txt”].each{|path|  
p path  
changed = false  
lines = IO.readlines( path ).map{|s|  
if s =~ /PROMOS:/  
old\_s = s.dup  
s.gsub!( reg\_exp, new\_text )  
changed = true if s != old\_s  
end  
s  
}

# Don’t write to file unless there was a change.

if changed  
File.open( path, “w” ){|f| f.puts lines }  
puts " —\> changed"  
end  
}

---

<div class="post-metadata">

**Author:** ![dcgrigsby](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/b5e925/32.png) [@dcgrigsby](https://www.ruby-forum.com/u/dcgrigsby)\
**Post date:** [September 6, 2007, 5:31pm UTC](https://www.ruby-forum.com/t/using-external-text-file-to-do-search-and-replace/112714/4 "2007-09-06T17:31:23Z")

</div>

On Aug 22, 5:43 pm, Stefano C. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> > This is what I have so far:  
> > end
> 
> Dir.glob(‘\*\*/\*.txt’).each do |path|
> 
> I hope this helps.
> 
> Stefano

I tried what you said and it shows it works and I get an Exit code: 0  
but nothing is modified.

Here’s what I have now:

require ‘yaml’  
promo = File.open(‘promo.yaml’)  
yp = YAML::load\_documents(promo) do |item|  
txt\_files = Dir.glob(‘\*\*/\*.txt’).each do |path|  
puts path  
file = File.open(path).readlines.each { |line|  
if line.match(/PROMOS/)  
then line.gsub!(item[‘search’], item[‘sub’])  
end }  
File.open(path, ‘w’){|f| f.write file}  
end  
end

And my YAML file looks like this:

* * *

search: /PROMO1=[A-Za-z0-9]+/  
sub: PROMO1=some\_text

Can anyone please tell me what’s wrong with it?

---

<div class="post-metadata">

**Author:** ![dcgrigsby](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/b5e925/32.png) [@dcgrigsby](https://www.ruby-forum.com/u/dcgrigsby)\
**Post date:** [August 25, 2007, 5:50pm UTC](https://www.ruby-forum.com/t/using-external-text-file-to-do-search-and-replace/112714/5 "2007-08-25T17:50:37Z")

</div>

Thanks everyone for your reply.

I’m visiting some friends at the moment so I can’t try anything yet  
but as soon as I get home I’ll try your suggestions, even if this will  
have to wait a few days. Maybe I can read the cookbook and docs for  
YAML till now.

Thank you again!

---

<div class="post-metadata">

**Author:** ![dcgrigsby](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/b5e925/32.png) [@dcgrigsby](https://www.ruby-forum.com/u/dcgrigsby)\
**Post date:** [September 6, 2007, 7:07pm UTC](https://www.ruby-forum.com/t/using-external-text-file-to-do-search-and-replace/112714/6 "2007-09-06T19:07:44Z")

</div>

Alle giovedì 6 settembre 2007, Dan G. ha scritto:

> file = File.open(path).readlines.each { |line|  
> search: /PROMO1=[A-Za-z0-9]+/  
> sub: PROMO1=some\_text
> 
> Can anyone please tell me what’s wrong with it?

The reason your code doesn’t work is that the search string is stored in  
the  
YAML file as a string, not as a regexp. To use it as a regexp, you need  
to  
create a regexp from it: remove the delimiting / in the regexp in the  
YAML  
file and replace the call to gsub! with:

gsub(Regexp.new(item[‘search’]), item[‘sub’])

and it should work.

Without creating the regexp, gsub! would look for a  
literal ‘/PROMO1=[A-Za-z0-9]+/’ in the argument, not for a Regexp. (As I  
wrote in my previous post, you can store directly a in the YAML file:

search: !ruby/regexp /PROMO1=[A-Za-z0-9]+/  
).

By the way, you don’t need to store the return value of  
YAML::load\_documents  
in a variable.

I hope this helps

Stefano

---

<div class="post-metadata">

**Author:** ![dcgrigsby](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/b5e925/32.png) [@dcgrigsby](https://www.ruby-forum.com/u/dcgrigsby)\
**Post date:** [September 7, 2007, 12:06am UTC](https://www.ruby-forum.com/t/using-external-text-file-to-do-search-and-replace/112714/7 "2007-09-07T00:06:29Z")

</div>

On Sep 6, 8:06 pm, Stefano C. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> > promo = File.open(‘promo.yaml’)
> 
> create a regexp from it: remove the delimiting / in the regexp in the YAML  
> search: !ruby/regexp /PROMO1=[A-Za-z0-9]+/  
> ).
> 
> By the way, you don’t need to store the return value of YAML::load\_documents  
> in a variable.
> 
> I hope this helps
> 
> Stefano

Thanks a lot for your help Stefano! It works great!

Cheers 🙂
