#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2005
    Posts
    5
    Rep Power
    0

    replace a string without overwriting the file?


    Ok, i am stuck. I run this program and it edits all the files I tell it to but it erases everything inside the file except for what it is replacing. What's weird is I have only changed the way old_string/new_string are inputted and how the 'installpath' variable is determined and the program went from preserving files to erasing their contents.

    The bad thing is I didn't make a backup of the working version of this before I added onto it and I don't remeber how it was differnt . So, does anyone know how I can read the files and replace the strings I want to in them WITHOUT erasing everything else in the file?

    Code:
    import fnmatch
    import fileinput
    import re
    import string
    import os.path
    import sys
    
    old_string = raw_input("Enter the old version number: ")
    new_string = raw_input("Enter the new version number: ")
    if old_string == new_string:
        print "Operation canceled due to same version number."
        sys.exit()
    installpath = raw_input("Where is the WoW Interface Folder? ")
    
    def callback(arg, directory, files):
        for file in files:
            if fnmatch.fnmatch(file,arg):
                for line in fileinput.input(os.path.abspath(os.path.join(directory, file)),inplace=1):
                    if re.search(old_string, line):
                        line = string.replace(line, old_string, new_string)
                        print line
                
    
    os.path.walk(installpath, callback, "*.toc") # Directory  you are looking under, and file pattern
    Peace out,

    -Kingdud
  2. #2
  3. No Profile Picture
    Contributing User
    Devshed Novice (500 - 999 posts)

    Join Date
    Nov 2003
    Posts
    624
    Rep Power
    34
    Change:
    Code:
    for line in fileinput.input(os.path.abspath(os.path.join(directory, file)),inplace=1):
        if re.search(old_string, line):
            line = string.replace(line, old_string, new_string)
            print line
    to:

    Code:
    for line in fileinput.input(os.path.abspath(os.path.join(directory, file)),inplace=1):
        if re.search(old_string, line):
            line = string.replace(line, old_string, new_string)
        print line
    (remove 4 spaces before 'print' to make it belong to the parent code block)

    Although I can't see how it saves anything to the files...?
    Last edited by sfb; February 17th, 2005 at 04:38 PM.
  4. #3
  5. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2005
    Posts
    5
    Rep Power
    0
    Yea, that didn't help :/. Interesting how moving that print line changes things though. I didn't know a print line could be significant I learned something, just not sure what. Anyone else have some ideas on how to get this sucker to overwrite one line without murdering the rest of the file?

    Peace out,

    -Kingdud
  6. #4
  7. Hello World :)
    Devshed Frequenter (2500 - 2999 posts)

    Join Date
    Mar 2003
    Location
    Hull, UK
    Posts
    2,537
    Rep Power
    69
    In short, you can't do this due to the way files are handled. The easiest way to do what you want to do is to read() the contents of the file in full, preform the replacement and write the whole string back to the file.

    A variation on this would be to read the whole file into a list using the readlines() method and iterate over them. You can now replace the specific line in this list and write the lines back to the file with the writelines() method.

    What about seek?

    Unfortunately, even if you seek to the specific position within the file any write operations will still wipe the file… or at least, everything after the seek position.

    Also, you seem to be using a lot of modules in your program for relatively little gain i.e. fnmatch could be replaced with a single call to the endswith() method of your string.

    You might want to rethink how your using these modules in your program. I'll give you a tip, you can solved this by using one of Pythons standard modules:

    http://www.python.org/doc/2.4/lib/module-os.html

    Note: that importing the os module also makes os.path available in your program. Check out os.walk() as a more efficient way to work with the directory tree .

    Hope this is of more help,

    Mark .
    programming language development: www.netytan.com Hula

  8. #5
  9. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2005
    Posts
    5
    Rep Power
    0
    K, this is really frying my brain why it works all of the sudden, but it does so I am not complaining. Probably some spacing issue...anywho, here is the working code:

    Code:
    import os.path
    import fnmatch
    import fileinput
    import re
    import string
    
    old_string = '## Interface: ' + raw_input('Old interface version number: ')
    new_string = '## Interface: ' + raw_input('New interface version number: ')
    path = raw_input('Where is the WoW Interface folder? ')
    installpath = os.path.normpath(path)
    
    def callback(arg, directory, files):
        for file in files:
            if fnmatch.fnmatch(file,arg):
                for line in fileinput.input(os.path.abspath(os.path.join(directory, file)),inplace=1):
                    if re.search(old_string, line): # I changed * to .* but it would probably work without this if
                        line = string.replace(line,old_string,new_string) # old string , new string
                    print line,
                
    
    os.path.walk(installpath, callback, "*.toc") # Directory  you are looking under, and file pattern
    That will find any string you specify (with some modification) and replace ONLY that string in the file and leave the rest of the file intact...I HAVE NO F*N CLUE WHY! *sigh* the fun of being new to programming.

    edit: WOW I AM A RETARD! I left of a space between ## and Interface, the line should have been '## Interface' instead of '##interface'. *cries*

    Peace out,

    -Kingdud
    Last edited by Kingdud; February 21st, 2005 at 11:38 AM. Reason: I am a retard...
  10. #6
  11. Contributing User
    Devshed Intermediate (1500 - 1999 posts)

    Join Date
    Dec 2004
    Location
    Meriden, Connecticut
    Posts
    1,797
    Rep Power
    154
    I didn't read all of netytan's post so I hope he didn't already mention this. Here is what I would do. Get all of the text from file, add it all to a list. (Sometimes it will gives each item in the list '\n' in front of it or at the end of it, if so, you can just do replace('\n', '')). After you've added all the text to a list. Replace the certain strings with whatever you want. Re-create (or clear, whatever) the file, and add all of the text from the list into that file. And now you've got your new text.
  12. #7
  13. No Profile Picture
    Contributing User
    Devshed Novice (500 - 999 posts)

    Join Date
    Nov 2003
    Posts
    624
    Rep Power
    34
    Interesting how moving that print line changes things though. I didn't know a print line could be significant
    The print line itself isn't significant, but the spacing is. Python uses it to denote 'blocks' of code.

    Using fileinput with inplace=1, it copies the file to a backup name, then redirects the standard output (e.g. print) to the new proper file name. Your original code had:

    Code:
    if re.search(old_string, line):
        line = string.replace(line, old_string, new_string)
        print line
    Which reads "If the old string is in the line, replace it then print the line, otherwise do not change or print the line". So it never gets printed.

    By moving the print line back, it moves outside the if: statement, and becomes:

    Code:
    if re.search(old_string, line):
        line = string.replace(line, old_string, new_string)
    print line
    Which reads "If the old string is in the line, replace it. Either way, print the line."

    If it works, that's good. If you want critique, read on

    - Using "import string, string.replace()" is very much the old, not really supported style. Instead, use line = line.replace()

    - Using re to find and match a static string is hugely overkill.
    "x" in "xyz" will do it, and is easier to write and read.

    - 'file' is the name of a built in function, and it's bad form to assign things over built ins, as in "for file in files", because it leads to confusion to people who are expecting it to mean something entirely different.

    - If there is one folder and no subfolders, glob.glob would be a neater module than os.path.walk, but it wont work with subdirectories.

    - You are using a bit of a mess of glob and os.path.walk to get the files you want to edit.

    If there is one folder, glob will work with:

    Code:
    import os.path
    import fileinput
    import glob
    
    old_string = '## Interface: ' + raw_input('Old interface version number: ')
    new_string = '## Interface: ' + raw_input('New interface version number: ')
    path = raw_input('Where is the WoW Interface folder? ')
    installpath = os.path.normpath(path)
    
    files = glob.glob(installpath + "\\*.toc")
    
    for f in files:
        for line in fileinput.input(installpath+"\\"+f, inplace=1):
            if old_string in line:
                line = line.replace(old_string, new_string)
            print line,
    (untested)


    If there are sub-folders, then use os.path.walk, but there's no need to bring fnmatch in as well:

    Code:
    import os.path
    import fileinput
    
    old_string = '## Interface: ' + raw_input('Old interface version number: ')
    new_string = '## Interface: ' + raw_input('New interface version number: ')
    path = raw_input('Where is the WoW Interface folder? ')
    installpath = os.path.normpath(path)
    
    def callback(arg, directory, files):
        for f in files:
            if f.endswith(".toc"):
                for line in fileinput.input(os.path.join(directory, f),inplace=1):
                    if old_string in line:
                        line = line.replace(old_string, new_string)
                    print line,
    
    os.path.walk(installpath, callback, None)
    (also untested)

    (I'm not sure the abspath is doing anything either)
    Last edited by sfb; February 21st, 2005 at 07:01 PM.

IMN logo majestic logo threadwatch logo seochat tools logo