简体   繁体   中英

How do I search a file for a string and replace it with multiple lines in Python?

I am running Python 3.5.1

I have a text file that I'm trying to search through and replace or overwrite text if it matches a predefined variable. Below is a simple example:

test2.txt

A Bunch of Nonsense Stuff
############################
# More Stuff Goes HERE     #
############################
More stuff here
Outdated line of information that has no comment above - message_label

The last line in this example needs to be overwritten so the new file looks like below:

test2.txt after script

A Bunch of Nonsense Stuff
############################
# More Stuff Goes HERE     #
############################
More stuff here

# This is an important line that needs to be copied
Very Important Line of information that the above line is a comment for - message_label

The function I have written idealAppend does not work as intended and subsequent executions create a bit of a mess. My workaround has been to separate the two lines into single line variables but this doesn't scale well. I want to use this function throughout my script with the ability to handle any number of lines. (if that makes sense)

Script

#!/usr/bin/env python3
import sys, fileinput, os
def main():

    file = 'test2.txt'
    fullData = r'''
# This is an important line that needs to be copied
Very Important Line of information that the above line is a comment for - message_label
'''
    idealAppend(file, fullData)


def idealAppend(filename, data):
    label = data.split()[-1]                                            # Grab last word of the Append String
    for line in fileinput.input(filename, inplace=1, backup='.bak'):
        if line.strip().endswith(label) and line != data:               # If a line 2 exists that matches the last word (label)
            line = data                                                 # Overwrite with new line, comment, new line, and append data.
        sys.stdout.write(line)                                          # Write changes to current line

    with open(filename, 'r+') as file:                                  # Open File with rw permissions
        line_found = any(data in line for line in file)                 # Search if Append exists in file

        if not line_found:                                              # If data does NOT exist
            file.seek(0, os.SEEK_END)                                   # Goes to last line of the file
            file.write(data)                                            # Write data to the end of the file


if __name__ == "__main__": main()

Workaround Script
This seems to work perfectly as long as I only need to write exactly two lines. I'd love this to be more dynamic when it comes to number of lines so I can reuse the function easily.

#!/usr/bin/env python3
import sys, fileinput, os
def main():

    file = 'test2.txt'
    comment = r'# This is an important line that needs to be copied'
    append = r'Very Important Line of information that the above line is a comment for - message_label'

    appendFile(file, comment, append)

def appendFile(filename, comment, append):

    label = append.split()[-1]                                          # Grab last word of the Append String
    for line in fileinput.input(filename, inplace=1, backup='.bak'):
        if line.strip().endswith(label) and line != append:             # If a line 2 exists that matches the last word (label)
            line = '\n' + comment + '\n' + append                       # Overwrite with new line, comment, new line, and append data.
        sys.stdout.write(line)                                          # Write changes to current line

    with open(filename, 'r+') as file:                                  # Open File with rw permissions
        line_found = any(append in line for line in file)               # Search if Append exists in file

        if not line_found:                                              # If data does NOT exist
            file.seek(0, os.SEEK_END)                                   # Goes to last line of the file
            file.write('\n' + comment + '\n' + append)                  # Write data to the end of the file

if __name__ == "__main__": main()

I am very new to Python so I'm hoping there is a simple solution that I overlooked. I thought it might make sense to try and split the fullData variable at the new line characters into a list or tuple, filter the label from the last item in the list, then output all entries but this is starting to move beyond what I've learned so far.

If I understand your issue correctly, you can just open the input and output files, then check whether the line contains old information and ends with the label and write the appropriate content accordingly.

with open('in.txt') as f, open('out.txt', 'r') as output:
    for line in f:
        if line.endswith(label) and not line.startswith(new_info):
            output.write(replacement_text)
        else:
            output.write(line)

If you want to update the original file instead of creating a second one, it's easiest to just delete the original and rename the new one instead of trying to modify it in place.

Is this what you are looking for ? It's looking for a label and then replaces the whole line with whatever you want.

test2.txt

A Bunch of Nonsense Stuff
############################
# More Stuff Goes HERE     #
############################
More stuff here

Here is to be replaced - to_replace

script.py

#!/usr/bin/env python3

def main():
    file = 'test2.txt'
    label_to_modify = "to_replace"
    replace_with = "# Blabla\nMultiline\nHello"
    """
    # Raw string stored in a file
    file_replace_with = 'replace_with.txt'
    with open(file_replace_with, 'r') as f:
        replace_with = f.read()
    """
    appendFile(file, label_to_modify, replace_with)

def appendFile(filename, label_to_modify, replace_with):
    new_file = []
    with open(filename, 'r') as f:
        for line in f:
            if len(line.split()) > 0 and line.split()[-1] == label_to_modify:
                new_file.append(replace_with)
            else:
                new_file.append(line)

    with open(filename + ".bak", 'w') as f:
        f.write(''.join(new_file))

if __name__ == "__main__": main()

test2.txt.bak

A Bunch of Nonsense Stuff
############################
# More Stuff Goes HERE     #
############################
More stuff here

# Blabla
Multiline
Hello

Reading over both answers I've come up with the following as the best solution i can get to work. It seems to do everything I need. Thanks Everyone.

#!/usr/bin/env python3

def main():

    testConfFile = 'test2.txt' # /etc/apache2/apache2.conf
    testConfLabel = 'timed_combined'
    testConfData = r'''###This is an important line that needs to be copied - ##-#-####
Very Important Line of information that the above line is a \"r\" comment for - message_label'''

    testFormatAppend(testConfFile, testConfData, testConfLabel)     # Add new test format

def testFormatAppend(filename, data, label):
    dataSplit = data.splitlines()
    fileDataStr = ''

    with open(filename, 'r') as file:
        fileData = stringToDictByLine(file)

    for key, val in fileData.items():
        for row in dataSplit:
            if val.strip().endswith(row.strip().split()[-1]):
                fileData[key] = ''

    fileLen = len(fileData) 
    if fileData[fileLen] == '':
        fileLen += 1
        fileData[fileLen] = data
    else:
        fileLen += 1
        fileData[fileLen] = '\n' + data

    for key, val in fileData.items():
        fileDataStr += val

    with open(filename, 'w') as file:
        file.writelines(str(fileDataStr))


def stringToDictByLine(data):
    fileData = {}
    i = 1
    for line in data:
        fileData[i] = line
        i += 1
    return fileData

if __name__ == "__main__": main()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM