tags:

views:

72

answers:

5

Hello,

I am trying to replace a string with a new string in a python file and write the new string permanently to it. When I run the below script it removes part of the string and not all of it. The string in the file is:

self.id = "027FC8EBC2D1"

And the script I have to replace the string is:

def edit():

    o = open("test.py","r+") #open 
    for line in open("test.py"):   
        line = line.replace("027FC8EBC2D1","NewValue")  
        o.write(line) 
    o.close()

edit()

Thanks for any help.

+1  A: 

You're opening the file as read-only and trying to write to it. And you've also got it open twice at once.

You'll want to reorganize it so that you've only got it open once, and that it is read-write.

Daniel DiPaolo
Nope. The OP uses `r+`, which is read/write.
Dirk
Yeah, it was a bit confusing because of the two `open` statements - the `for` loop one is read only, but the one he writes to is `r+`. The confusing nature of having both file handles open means it probably just deserves a reorg
Daniel DiPaolo
+2  A: 

Try something like this:

import fileinput
for line in fileinput.input('test.py', inplace=1):
  line.replace("027FC8EBC2D1","NewValue")

That makes your call to fh.close() extraneous (the with handles it) and prevents you from having multiple copies of the file open at a time.

g.d.d.c
@eksortso Thanks! I only started using Python at 2.6. I wasn't aware `with` had to be imported manually prior.
g.d.d.c
@g.d.d.c -- I tried this code but it comes up with an error saying string object has no write(). I changed it so that it was fh.write(line). But this appended the NewValue to the file.
chrissygormley
@chrissygormley You're correct, it did not behave quite as I expected. I've updated with an example that uses the fileinput module. Thanks,
g.d.d.c
@g.d.d.c `fileinput` is a good way to go. But you should `print` the string that you produce, `rstrip` that string or otherwise print it without the line separator, and provide `input` with a `backup` parameter just in case the output isn't right and you need to restore your original files.
eksortso
+5  A: 

You cannot safely do what you intend to do, unless the replacement value and the original value have exactly the same length. Unless this is guaranteed, I'd copy the file:

with open('input.txt', 'r') as in_file:
    with open('output.txt', 'w') as out_file:
        for line in in_file:
           line = line.replace('027FC8EBC2D1', 'NewValue')
           out_file.write(line)

EDIT (removed misleading information)

Dirk
`line` contains end of line character, you don't need to add it again.
SilentGhost
@SilentGhost: Good catch. Answer edited. Thanks.
Dirk
+2  A: 

The proper way to do (actually simulate;-) "in-place substitution" on text files with Python is the fileinput module:

import fileinput

for line in fileinput.input(['test.py'], inplace=True):
  print line.replace('027FC8EBC2D1', 'NewValue'),

Note a couple of crucial details wrt the other answer that suggests the same module: input's first argument must be a list of filenames (not a string!), and, you do have to print every line that you want in the resulting file (fileinput redirects standard output to perform -- actually simulate -- the "overwrite in-place" effect).

A final small but not-negligible detail: the comma at the end of the print statement is to avoid adding another newline at the end (since each line already ends with a newline!-).

Alex Martelli
+1  A: 

If you've got enough memory, compared to the size of your file, you can actually get away with this:

# Open the file for read/write access
f = open( 'test.py', 'r+' )

# Read the entire contents of the file into memory
data = f.read()

# Replace the old value with the new one
data.replace( '027FC8EBC2D1', 'NewValue' )

# Truncate the file (in case NewValue is shorter than OldValue)
f.truncate( 0 )

# Write all the data out again
f.write( data )

# Close the file
f.close()

I wouldn't recommend it for very large files, but it's going to be a faster solution than you might otherwise expect.

Craig Trader