2015-02-24 5 views
0

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

+1

'изменение регистра строки может изменить свою length': Можете ли вы подробнее рассказать о том, что ..? –

+0

@mu, U + 00DF LATIN SMALL LETTER SHARP S становится «SS» при преобразовании в верхний регистр. U + 0130 LATIN CAPITAL LETTER I С DOT ABOVE становится двумя символами (U + 0069 LATIN SMALL LETTER I и U + 0307 COMBINING DOT ABOVE) при преобразовании в нижний регистр. – Hammerite

+0

Это питон 2 или 3? –

ответ

3

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

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.91s

Для длинных строк (len(LL) == 600 символов) более 1 млн повторений:

  • lower() метод: 2.54s
  • re метода: 1.96S (победитель)

Unicode символы, сочетающих

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

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

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

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

См. Мое редактирование на вопрос, почему ваша модификация do_thing() не будет работать. – Hammerite

+0

@Hammerite Просмотреть мои обновления –

0

Просто используйте 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()