почему для длинных строк IndexError

есть функция для транслитерации польских слов

def transcript(s: str) -> str:
    result = ''
    table = {'a': 'а', 'la': 'я', 'ą': 'он', 'ąb': 'ом', 'ąp': 'ом', 'lą': 'ьон', 'ląp': 'ьом', 'ląb': 'ьом', 'b': 'б',
             'c': 'ц', 'сh': 'х', 'cz': 'ч', 'ć': 'ць', 'd': 'д', 'dz': 'дз', 'dź': 'дзь', 'dż': 'дж', 'e': 'е',
             'ę': 'ен', 'ęb': 'ем', 'ęp': 'ем', 'f': 'ф', 'g': 'ґ', 'h': 'г', 'i': 'і', 'ia': 'я', 'bia': '’я',
             'pia': '’я', 'wia': '’я', 'ią': 'ьон', 'bią': 'йон', 'pią': 'йон', 'wią': 'йон', 'iąp': 'йом',
             'iąb': 'йом', 'ię': 'єн', 'bię': '’єн', 'pię': '’єн', 'mię': '’єн', 'ięb': 'єм', 'ięp': 'єм',
             'bięb': '’єм', 'pięb': '’єм', 'mięb': '’єм', 'bięp': '’єм', 'pięp': '’єм', 'mięp': '’єм', 'io': 'ьо',
             'ió': 'ю', 'pió': '’ю', 'bió': '’ю', 'wió': '’ю', 'iu': 'ю', 'j': 'й', 'ja': 'я', 'ją': 'йон',
             'jąb': 'ьом', 'jąp': 'ьом', 'bją': 'йом', 'pją': 'йом', 'je': 'є', 'ję': '’єн', 'jęb': 'єм', 'jęp': 'єм',
             'jo': 'йо', 'jó': 'ью', 'ju': 'ю', 'k': 'к', 'l': 'л', 'ł': 'л', 'm': 'м', 'n': 'н', 'ń': 'нь', 'o': 'о',
             'lo': 'у', 'ló': 'ю', 'p': 'п', 'r': 'р', 'z': 'з', 'rz': 'ж', 'chrz': 'ш', 'krz': 'ш', 'prz': 'ш',
             'trz': 'ш', 'rzch': 'ш', 'rzk': 'ш', 'rzp': 'ш', 'rzt': 'ш', 's': 'с', 'sz': 'ш', 'szcz': 'щ', 'ś': 'сь',
             't': 'т', 'u': 'у', 'lu': 'ю', 'w': 'в', 'y': 'и', 'z': 'з', 'ź': 'зь', 'ż': 'ж', 'ó': 'у',
             }

    i = 0
    while True:
        if i > len(s):
            break

        if s[i:i + 4] in table:
            if s[i:i + 4][0].isupper():
                result += table[s[i:i + 4].lower()].capitalize()
            else:
                result += table[s[i:i + 4]]
            i += 4
        elif s[i:i + 3] in table:
            if s[i:i + 3][0].isupper():
                result += table[s[i:i + 3].lower()].capitalize()
            else:
                result += table[s[i:i + 3]]
            i += 3
        elif s[i:i + 2] in table:
            if s[i:i + 2][0].isupper():
                result += table[s[i:i + 2].lower()].capitalize()
            else:
                result += table[s[i:i + 2]]
            i += 2
        elif s[i] in table:
            if s[i].isupper():
                result += table[s[i].lower()].upper()
            else:
                result += table[s[i]]
            i += 1
        else:
            if s[i].isalpha():
                if s[i][0].isupper():
                    result += table[s[i].lower()].upper()
                else:
                    result += s[i]
            else:
                result += s[i]
            i += 1

    return result

для кратких строк к примеру Od niedawna szósty stycznia jest работает отлично, но когда передать к примеру Od niedawna szósty stycznia jest znowu w Polsce wolnym dniem. To bardzo stare święto, związane z Bożym Narodzeniem. Obchodzi się je na pamiątkę mędrców ze Wschodu, którzy za Gwiazdą przyszli do Betlejem i złożyli dary Jezusowi. Popularnie nazywani są: Kacper, Melchior i Baltazar. Na drzwiach wejściowych pisze się kredą ich inicjały oraz aktualny rok. Wielkanoc to najważniejsze święto chrześcijańskie upamiętniające Zmartwychwstanie Jezusa Chrystusa. To święto ruchome, obchodzi się je w pierwszą niedzielę po pierwszej wiosennej pełni księżyca. Najwcześniej dwudziestego drugiego marca, a najpóźniej dwudziestego piątego kwietnia. получаю ошибку

Traceback (most recent call last): File "/Users/andriykmit/PycharmProjects/pol-ua/main.py", line 59, in print(transcript('''Od niedawna szósty stycznia jest znowu w Polsce wolnym dniem. To bardzo stare święto, związane z Bożym Narodzeniem. Obchodzi się je na pamiątkę mędrców ze Wschodu, którzy za Gwiazdą przyszli do Betlejem i złożyli dary Jezusowi. Popularnie nazywani są: Kacper, Melchior i Baltazar. Na drzwiach wejściowych pisze się kredą ich inicjały oraz aktualny rok. File "/Users/andriykmit/PycharmProjects/pol-ua/main.py", line 40, in transcript elif s[i] in table: IndexError: string index out of range


Ответы (3 шт):

Автор решения: Эникейщик

Ошибка возникает, если строка заканчивается не на букву (а на точку или пробел итд). От длины строки не зависит.

→ Ссылка
Автор решения: GrAnd

Заменить

    while True:
        if i > len(s):
            break

на

    while i < len(s):

Тогда не будет вылета за границу строки.

Вторая ошибка - нет приведения к нижнему регистру когда делается
if s[i:i + 4] in table. Соответственно, при первой заглавной букве до последующего условия if s[i:i + 4][0].isupper() дело не дойдёт.


Ну и вообще, все замены можно в один регексп уложить:

import re
def transcript(s: str) -> str:
    table = {'a': 'а', 'la': 'я', 'ą': 'он', 'ąb': 'ом', 'ąp': 'ом', 'lą': 'ьон', 'ląp': 'ьом', 'ląb': 'ьом', 'b': 'б',
             'c': 'ц', 'сh': 'х', 'cz': 'ч', 'ć': 'ць', 'd': 'д', 'dz': 'дз', 'dź': 'дзь', 'dż': 'дж', 'e': 'е',
             'ę': 'ен', 'ęb': 'ем', 'ęp': 'ем', 'f': 'ф', 'g': 'ґ', 'h': 'г', 'i': 'і', 'ia': 'я', 'bia': '’я',
             'pia': '’я', 'wia': '’я', 'ią': 'ьон', 'bią': 'йон', 'pią': 'йон', 'wią': 'йон', 'iąp': 'йом',
             'iąb': 'йом', 'ię': 'єн', 'bię': '’єн', 'pię': '’єн', 'mię': '’єн', 'ięb': 'єм', 'ięp': 'єм',
             'bięb': '’єм', 'pięb': '’єм', 'mięb': '’єм', 'bięp': '’єм', 'pięp': '’єм', 'mięp': '’єм', 'io': 'ьо',
             'ió': 'ю', 'pió': '’ю', 'bió': '’ю', 'wió': '’ю', 'iu': 'ю', 'j': 'й', 'ja': 'я', 'ją': 'йон',
             'jąb': 'ьом', 'jąp': 'ьом', 'bją': 'йом', 'pją': 'йом', 'je': 'є', 'ję': '’єн', 'jęb': 'єм', 'jęp': 'єм',
             'jo': 'йо', 'jó': 'ью', 'ju': 'ю', 'k': 'к', 'l': 'л', 'ł': 'л', 'm': 'м', 'n': 'н', 'ń': 'нь', 'o': 'о',
             'lo': 'у', 'ló': 'ю', 'p': 'п', 'r': 'р', 'z': 'з', 'rz': 'ж', 'chrz': 'ш', 'krz': 'ш', 'prz': 'ш',
             'trz': 'ш', 'rzch': 'ш', 'rzk': 'ш', 'rzp': 'ш', 'rzt': 'ш', 's': 'с', 'sz': 'ш', 'szcz': 'щ', 'ś': 'сь',
             't': 'т', 'u': 'у', 'lu': 'ю', 'w': 'в', 'y': 'и', 'z': 'з', 'ź': 'зь', 'ż': 'ж', 'ó': 'у',
             }

    return re.sub("|".join(map(re.escape, sorted(table, key=len, reverse=True))), lambda m: table[m.group(0).lower()].capitalize() if m.group(0)[0].isupper() else table[m.group(0).lower()], s, flags=re.I) 
→ Ссылка
Автор решения: TigerTV.ru

Немного сократил повторы кода и задействовал join для строк:

def transcript(s: str) -> str:
    table = {'a': 'а', 'la': 'я', 'ą': 'он', 'ąb': 'ом', 'ąp': 'ом', 'lą': 'ьон', 'ląp': 'ьом', 'ląb': 'ьом', 'b': 'б',
             'c': 'ц', 'сh': 'х', 'cz': 'ч', 'ć': 'ць', 'd': 'д', 'dz': 'дз', 'dź': 'дзь', 'dż': 'дж', 'e': 'е',
             'ę': 'ен', 'ęb': 'ем', 'ęp': 'ем', 'f': 'ф', 'g': 'ґ', 'h': 'г', 'i': 'і', 'ia': 'я', 'bia': '’я',
             'pia': '’я', 'wia': '’я', 'ią': 'ьон', 'bią': 'йон', 'pią': 'йон', 'wią': 'йон', 'iąp': 'йом',
             'iąb': 'йом', 'ię': 'єн', 'bię': '’єн', 'pię': '’єн', 'mię': '’єн', 'ięb': 'єм', 'ięp': 'єм',
             'bięb': '’єм', 'pięb': '’єм', 'mięb': '’єм', 'bięp': '’єм', 'pięp': '’єм', 'mięp': '’єм', 'io': 'ьо',
             'ió': 'ю', 'pió': '’ю', 'bió': '’ю', 'wió': '’ю', 'iu': 'ю', 'j': 'й', 'ja': 'я', 'ją': 'йон',
             'jąb': 'ьом', 'jąp': 'ьом', 'bją': 'йом', 'pją': 'йом', 'je': 'є', 'ję': '’єн', 'jęb': 'єм', 'jęp': 'єм',
             'jo': 'йо', 'jó': 'ью', 'ju': 'ю', 'k': 'к', 'l': 'л', 'ł': 'л', 'm': 'м', 'n': 'н', 'ń': 'нь', 'o': 'о',
             'lo': 'у', 'ló': 'ю', 'p': 'п', 'r': 'р', 'z': 'з', 'rz': 'ж', 'chrz': 'ш', 'krz': 'ш', 'prz': 'ш',
             'trz': 'ш', 'rzch': 'ш', 'rzk': 'ш', 'rzp': 'ш', 'rzt': 'ш', 's': 'с', 'sz': 'ш', 'szcz': 'щ', 'ś': 'сь',
             't': 'т', 'u': 'у', 'lu': 'ю', 'w': 'в', 'y': 'и', 'z': 'з', 'ź': 'зь', 'ż': 'ж', 'ó': 'у',
             }

    res = []
    i = 0
    while i < len(s):
        for j in range(4, 0, -1):
            m = s[i:i + j].lower()
            if m in table:
                if s[i].isupper():
                    res.append(table[m].capitalize())
                else:
                    res.append(table[m])
                i += j 
                break
        else:
            res.append(s[i])
            i += 1

    return "".join(res)


print(transcript("abcd bia. Cd Cz CZ cz cZ."))

# абцд ’я. Цд Ч Ч ч ч.
→ Ссылка