Как мне удалить одну строку из начала другой, если я знаю, что более длинная строка соответствует регистронезависимому?

Предположим, у меня есть рабочий процесс, который включает проверку начала длинной строки (LSскажем) чтобы увидеть, начинается ли оно с более короткой строки SS, Если это произойдет, я отрезаю соответствующую часть LS и сделай что-нибудь с оставшейся частью. В противном случае я делаю что-то еще. (Конкретным случаем, который вызвал этот вопрос, была библиотека разбора.)

def do_thing(LS, SS):
    if (LS.startswith(SS)):
        action_on_match(LS[len(SS):])
    else:
        action_on_no_match()

Это просто. Теперь, допустим, я хочу сделать то же самое, но на этот раз я хочу, чтобы строки соответствовали без учета регистра. Можно ли проверитьLS.startswith(SS) но без учета регистра ". Но как я должен определить, сколько LS "отрубить", когда я передаю его action_on_match()? Недостаточно просто использовать len(SS) как это было раньше, потому что, если я пишу в верхнем или нижнем регистре или складываю дела, то длина соответствующего префикса LS может не соответствовать ожиданиям: изменение регистра строки может изменить ее длину. Важно, чтобы часть LS перешел к action_on_match() быть именно тем, что программа получила в качестве входных данных (конечно, после точки отсечения).


Ответчики предложили использовать lower() и сохранение использования len(SS), но это не сработает

Python 3.4.2 (v3.4.2:ab2c023a9432, Oct  6 2014, 22:15:05) [MSC v.1600 32 bit (In
tel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> def action_on_match (s): return "Match: %s" % s
...
>>> def action_on_no_match (): return "No match"
...
>>> def do_thing (LS, SS):
...     if LS.lower().startswith(SS.lower()):
...         return action_on_match(LS[len(SS):])
...     else:
...         return action_on_no_match()
...
>>> do_thing('i\u0307asdf', '\u0130')
'Match: \u0307asdf'
>>>

Здесь мы ожидаем увидеть 'Match: asdf', но есть лишний персонаж.

2 ответа

Решение

Достаточно просто:

def do_thing(LS, SS):
    if LS.lower().startswith(SS.lower()):
        action_on_match(LS[len(SS):])
    else:
        action_on_no_match()

Все, что я делаю, это нижний регистр обоих LS а также SS а затем сравнивая их. Это будет намного медленнее, чем решение регулярных выражений для очень длинных строк, так как сначала нужно преобразовать всю строку в нижний регистр.

Решение для регулярных выражений будет выглядеть так:

import re

def do_thing(LS, SS):
    if re.match("^%s" % SS, LS, re.I):
        action_on_match(LS[len(SS):])
    else:
        action_on_no_match()

Спектакль

Для коротких струн (len(LL) == 8 символов) более 1000000 итераций:

  • lower() Метод: 0,86 (победитель)
  • re метод: 1,91 с

Для длинных струн (len(LL) == 600 символов) более 1000000 итераций:

  • lower() метод: 2,54 с
  • re Метод: 1,96 (победитель)

Юникод, объединяющий символы

Для символов объединения Юникод данные должны быть сначала нормализованы. Это означает преобразование любого предварительно составленного символа в его составные части. Вы найдете, например:

>>> '\u0130' == 'I\u0307'
False
>>> normalize("NFD", '\u0130') == normalize("NFD", 'I\u0307')
True

Вам нужно будет выполнить этот процесс нормализации на ваших входах:

SS = normalize("NFD", SS)
LS = normalize("NFD", LS)

Просто используйте str.lowerдлина "FOO" будет такой же, как "foo".lower():

LS.lower().startswith(SS.lower())



def do_thing(ls, ss):
    if ls.startswith(ss):
        action_on_match(ls[len(ss):])
    else:
        action_on_no_match()
Другие вопросы по тегам