Stack Exchange
Compare strings to markup commentary in main text
from itertools import cycle

# Integers represent Chinese characters
main_text = "234458674935346547521456767378878473562561423"
text_with_commentary = "2344586749215432678652353465475214561655413164653413216576737887847356152352561423"

iter_main = iter(main_text)
iter_comm = iter(text_with_commentary)

marked_up = []
x = next(iter_main)
y = next(iter_comm)
i = 0
j = 0

def commentary_start():
    global x, y, i, j, marked_up

    while x == y and j < len(text_with_commentary):
        try:
            marked_up.append(y)
            x = next(iter_main)
            y = next(iter_comm)
            i += 1
            j += 1
        except: return

    marked_up.append("{{")
            
    return marked_up

def commentary_end():
    global x, y, i, j, marked_up

    while x != y:
        marked_up.append(y)
        y = next(iter_comm)
        j += 1
        
    if main_text[i+1] == text_with_commentary[j+1] and \
    main_text[i+2] == text_with_commentary[j+2]:
        marked_up.append("}}")
        
    else: 
        marked_up.append(y)
        y = next(iter_comm)
        j += 1
        commentary_end()
    
    return marked_up

def mark_up_commentary():
    global marked_up
    
    while j < len(text_with_commentary):
        try:
            commentary_start()
            commentary_end()
        except:
            break
            
    marked_up = "".join(marked_up)
    
    return marked_up



mark_up_commentary()

# Expected result: "2344586749{{215432678652}}35346547521456{{16554131646534132165}}76737887847356{{15235}}2561423"


The above code compares two sets of strings, character by character, to markup commentary text that is interspersed between the main text.
The code makes use of the iterators iter_main and inter_comm to check if the next character in the commented text is the same as the next character of the main text, adding double braces to mark up commentary text that deviates from the main text at the relevant positions.
I would like to seek help to improve the code in these respects:

See if the code can be made more concise and efficient.
The current method is actually quite hard-coded and mechanical, which makes little room for minor deviations in the texts, which is actually unavoidable in the real world. I would hence like to know whether fuzzy text-comparison tools that make use of concepts such as Levenshtein distance can be implemented to make the code more robust and flexible.
Are there good libraries to recommend for the task?



Are there good libraries to recommend for the task?

Yes, and it's built-in: read about difflib.

I would hence like to know whether fuzzy text-comparison tools [...] can be implemented

Off-topic for CodeReview.
Avoid globals, use difflib, and this becomes
from difflib import SequenceMatcher
from typing import Iterable


def diff(main_text: str, text_with_commentary: str) -> Iterable[str]:
    matcher = SequenceMatcher(a=main_text, b=text_with_commentary, autojunk=False)
    for op, a0, a1, b0, b1 in matcher.get_opcodes():
        right = text_with_commentary[b0: b1]
        if op == 'equal':
            yield right
        elif op == 'insert':
            yield '{' + right + '}'
        else:
            raise ValueError(f"{op} indicates malformed text")


def test():
    actual = ''.join(
        diff(
            main_text=(
                "2344586749"
                "35346547521456"
                "76737887847356"
                "2561423"
            ),
            text_with_commentary=(
                "2344586749"
                "215432678652"
                "35346547521456"
                "16554131646534132165"
                "76737887847356"
                "15235"
                "2561423"
            ),
        )
    )

    expected = (
        "2344586749"
        "{215432678652}"
        "35346547521456"
        "{16554131646534132165}"
        "76737887847356"
        "{15235}"
        "2561423"
    )
    assert actual == expected


Could you explain the logic of the code to me? This is the first time I've come across a generator function. Seems like I am supposed to use it with ''.join([i for i in diff(main_text, text_with_commentary)]).

Close, but it would just be ''.join(diff(....)). It's just yielding strings that the user can choose to do with it what they like - join, or otherwise.

What does Iterable[str] at the function annotation do? Is it there just to indicate that this function returns an iterable string?

An iterable of strings - in other words, if you for x in diff(), x will be a string on each iteration.

The variables op, a0, a1, b0, b1 seem to map to my globals marked_up, x, y, i, j, but not exactly. What are the differences between these two sets of variables other than globality?

They have totally different purposes. Read about get_opcodes here: https://docs.python.org/3/library/difflib.html#difflib.SequenceMatcher.get_opcodes

SequenceMatcher(None, a, b) would return a tuple that describes how string a transcribes to string b. op is the tag, while a0, a1 and  b0, b1 are the position indicators of the stretch of matching or mismatching strings between a and b. Is this understanding correct?

Let us continue this discussion in chat.
