Search code examples
pythoniterationpython-docxscoping

Python-docx replacing string with consecutive numbering


I have a document where I put "#" as a placeholder which I want to replace with consecutive numbering

For example:

# Foo
# Bar 
# Baz
# Hello #

Would be replaced with:

1 Foo
2 Bar 
3 Baz
4 Hello 5

Here is the code I tried it will replace the variables all with 0 due to a scoping issue:

from docx import Document

document = Document('old.docx')

for p in document.paragraphs:
    inline = p.runs
    key = "#"
    count = 0
    for i in range(len(inline)):
        text = inline[i].text
        if key in text:

             text=text.replace(key,str(count),1)
             inline[i].text = text
             count += 1  #Not updating due to scoping issue


document.save('new.docx')

Solution

  • I am pretty sure its not a scoping issue and more like a small looping error. Fixed it and tested.

    from docx import Document
    
    document = Document('old.docx')
    count = 1 #Initialize the count here.
    for p in document.paragraphs: #apparently each line is a paragraph
        inline = p.runs
        key = "#"
        #count = 0 #noticed that this was resetting to 0....at each line
        for i in range(len(inline)):
            text = inline[i].text #This actually returns the entire line of characters I.e.(# Bar)
            for char in range(len(text)): #so now we need to go through the characters in the line
            
                if key == text[char]: #IF THE key is the character we replace the character with our count
    
                    text=text.replace(key,str(count),1)
                    count += 1  #It was resetting in your loop earlier
                inline[i].text = text #Now we reassign the entire char chain to inline.
    
    document.save('new.docx')
    

    Lets consider a solution for a string. I would simply use regex because its more versatile in my opinion.

    from docx import Document
    import re
    from itertools import count
    
    document = Document('Foo.docx')
    occurences = 0
    counter = count(occurences)
    marker_string = "replace_me" #Just for show (You can do for ANY type of char/string or sets of string)
    target_expression = re.compile("replace\_me") #note the escape for special characters
    
    for p in document.paragraphs:
        inline = p.runs
        #count = 0
        for i in range(len(inline)):
            text = inline[i].text
            newtext = re.sub(target_expression,lambda x:  str(next(counter)),text) #we use regex sub function with lambda 
            #to sub in our iter tools counter.
            inline[i].text = newtext
                   
    
    
    document.save('new.docx')