鉴于我知道较长的字符串不区分大小写匹配,我应该如何从另一个字符串的开头删除一个字符串?

How should I remove one string from the start of another given that I know the longer string matches case-insensitively?

假设我有一个工作流程,其中涉及检查长字符串(例如 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) 但不区分大小写”。但是当我将它传递给action_on_match()时,我应该如何确定LS到"chop off"的多少?像以前一样只使用 len(SS) 是不够的,因为如果我是大写或小写或 casefolding 东西,那么 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',但是多了一个字符

只需使用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()

足够简单:

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

我所做的只是将 LSSS 都小写,然后比较它们。对于非常长的字符串,这将比正则表达式解决方案慢得多,因为它必须先将整个字符串转换为小写。

正则表达式解决方案如下所示:

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()

性能

对于超过 1000000 次迭代的短字符串(len(LL) == 8 个字符):

  • lower()方法:0.86s(获胜者)
  • re方法:1.91s

对于超过 1000000 次迭代的长字符串(len(LL) == 600 个字符):

  • lower()方法:2.54s
  • re 方法:1.96s (获胜者)

Unicode 组合字符

对于 unicode 组合字符,数据需要 normalised first. This means converting any precomposed character 成其组成部分。你会发现例如:

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

您需要对您的输入执行此规范化过程:

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