Как мне удалить одну строку из начала другой, если я знаю, что более длинная строка соответствует регистронезависимому?
Предположим, у меня есть рабочий процесс, который включает проверку начала длинной строки (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()