[Tutor] Search and Replace

Roeland Rengelink r.b.rigilink@chello.nl
Thu, 28 Jun 2001 08:17:31 +0200


Hi Van,

A couple of comments, and the version I use, which also has a couple of
problems.

Why use regular expressions, when there is a perfectly good
string.replace() function that does exactly what you want?

For the inner loop, how about:

for i in xrange(len(linelist)):
    linelist[i] = matcher.sub(rtext, linelist[i])
    # or linelist[i].replace(stext, rtext)
file = open(fname, 'w')
file.writelines(linelist)

listonly and skipext are globals, why not do these tests before you call
visitfile?

if not listonly and ext not in skipext:
    visitfile(fname)

I think the biggest nono in this script, is silently catching all
errors. You will never notice anything going wrong (undefined listonly
and skipexts, for example, but also erorrs in opening, reading and
writing files)

At least do something like:
except:
    debug('An error occured, ignoring...')

VanL wrote:
> 
> Hello,
> 
> I just did a search and replace script.  It works, but I am wondering if
> there is something better.
> 
> I use os.path.walk to walk through a directory tree, calling the
> visitfile function on each file (quoted below).
> 
> By the way, I know that I scan through the file multiple times; I have a
> version that doesn't do that, but I did it this way first to get a
> little better debugging output.  Just ignore that inefficiency.
> 

Actually, it's a good reason, I do too, see below.

> def visitfile(fname, sarstrings):
>         stext, rtext = sarstrings[0], sarstrings[1]
>         matcher = re.compile(stext)
>         try:
>                 if not listonly:
>                         if os.path.splitext(fname)[1] in skipexts:
>                                 debug('Skipping: ' + fname)
>                         elif string.find(open(fname).read(), stext) != -1:
>                                 debug('%s has %s' % (fname, stext))
>                                 linelist = open(fname, 'rb+').readlines()
>                                 file = open(fname, 'wb', 0)
>                                 for line in linelist:
>                                         line = matcher.sub(rtext, line)
>                                         file.write(line)
>                                 file.close()
>                                 fcount = fcount + 1
>         except: pass
> 
> Like I said, this works... but it worries me a little because I truncate
> the file to 0 length and then rewrite it from the in-memory list.  This
> could very easily be a problem for very large files.  Moreover, if the
> script is interrupted, the curent file will be lost.
> 
> Is there a better way to do an in-place change?
> 
> Thanks,
> 
> Van
> 
> _______________________________________________
> Tutor maillist  -  Tutor@python.org
> http://mail.python.org/mailman/listinfo/tutor

My version

Ugly, no function, not reusable, bad exception handling, but hey, it
works.
An in-memory copy of the file is also not very efficient. But I really
only want to replace the file if anything is changed.

better inner loop:

for i in xrange(len(lines)):
    if lines[i].find(repl_old) != -1:
        changed = 1
    lines[i] = lines[i].replace(repl_old, repl_new)


#!/data/software/bin/python
import sys

if len(sys.argv) < 4:
    print 'Usage : file_replace replace_old replace_new file1 file2 ...
filen'
    
repl_old = sys.argv[1]
repl_new = sys.argv[2]
filelist = sys.argv[3:]

for filename in filelist:
    try:
        f = open(filename,'r')
        new_lines = []
        changed = 0
        lines = f.readlines()
        for l in lines:
            nl = l.replace(repl_old, repl_new)
            new_lines.append(nl)
            if nl != l:
                changed = 1
        if changed:
            print 'Updating :', filename
            f = open(filename,'w')
            f.writelines(new_lines)
    except:
        print 'Error replacing %s with %s in %s' % (repl_old, repl_new,
filename)
 
Hope this helps,

Roeland

-- 
r.b.rigilink@chello.nl

"Half of what I say is nonsense. Unfortunately I don't know which half"