6

如果我有一个可以附加到字符串的前缀列表,我如何将一个字符串拆分为它的前缀和下一个子字符串中的其他字符。例如:

prefixes = ['over','under','re','un','co']

str1 = "overachieve"
output: ["over","achieve"]

str2 = "reundo"
output = ["re","un","do"]

有没有更好的方法来完成上述任务,也许使用正则表达式或一些字符串函数,而不是:

str1 = "reundo"
output = []

for x in [p for p in prefixes if p in str1]:
    output.append(x)    
    str1 =  str1.replace(x,"",1)
output.append(str1)
4

5 回答 5

5

正则表达式是搜索许多替代前缀的有效方法:

import re

def split_prefixes(word, prefixes):
    regex = re.compile('|'.join(sorted(prefixes, key=len, reverse=True)))
    result = []
    i = 0
    while True:
        mo = regex.match(word, i)
        if mo is None:
            result.append(word[i:])
            return result
        result.append(mo.group())
        i = mo.end()


>>> prefixes = ['over', 'under', 're', 'un', 'co']
>>> for word in ['overachieve', 'reundo', 'empire', 'coprocessor']:
        print word, '-->', split_prefixes(word, prefixes)

overachieve --> ['over', 'achieve']
reundo --> ['re', 'un', 'do']
empire --> ['empire']
coprocessor --> ['co', 'processor']
于 2013-01-28T09:05:34.603 回答
1

我会使用该str.startswith方法

for p in prefixes:
    if str1.startswith(p):
        output.append(p)
        str1 = str1.replace(p, '', 1)
output.append(str1)

您的代码的最大缺陷是类似的字符串'found'会输出['un', 'fod'].

但是,如果您有一个假设的 string 'reuncoundo',那么您将需要多次迭代列表。

while True:
    if not any(str1.startswith(i) for i in prefixes):
        output.append(str1)
        break
    for p in prefixes:
        if str1.startswith(p):
            output.append(p)
            str1 = str1.replace(p, '', 1)

这输出['re', 'un', 'co', 'un', 'do']

于 2013-01-28T08:30:19.030 回答
1
prefixes = ['over','under','re','un','co']

def test(string, prefixes, existing=None):
    prefixes.sort(key = lambda s: len(s))
    prefixes.reverse() # This and the previous line ensure that longer prefixes are searched first regardless of initial sorting.
    if existing is None:
        existing = [] # deals with the fact that placing [] as a default parameter and modifying it modifies it for the entire session
    for prefix in prefixes:
        if string.startswith(prefix):
            existing.append(prefix)
            return test(string[len(prefix):], prefixes, existing)
    existing.append(string)
    return existing

这段代码递归地遍历一个字符串,删除已知的前缀直到它用完,然后返回整个列表。在较长的字符串上,生成器可能是更好的路线,但在较短的字符串上,不需要生成器的额外开销可能会使这成为更好的解决方案。

于 2013-01-28T08:32:59.353 回答
1

考虑到“两个问题”的谚语,我仍然会说这是正则表达式的工作。正则表达式编译为状态机,该状态机并行检查所有可能的变体,而不是一个一个地检查。

这是一个利用它的实现:

import re

def split_string(string, prefixes):
    regex = re.compile('|'.join(map(re.escape, prefixes))) # (1)
    while True:
        match = regex.match(string)
        if not match:
            break
        end = match.end()
        yield string[:end]
        string = string[end:]
    if string:
        yield string # (2)

prefixes = ['over','under','re','un','co']
assert (list(split_string('recouncoundo',prefixes))
        == ['re','co','un','co','un','do'])

注意正则表达式在 (1) 中是如何构造的:

  • 前缀使用转义,re.escape以便特殊字符不会干扰
  • |使用(或)正则表达式运算符连接转义的前缀
  • 整个事情都被编译了。

第 (2) 行产生最后一个词,如果在拆分前缀后有剩余的话。if string如果您希望函数在前缀剥离后没有任何内容时返回空字符串,您可能需要删除检查。

另请注意,re.match(与 相反re.search)仅在输入字符串的开头查找模式,因此无需附加^到正则表达式。

于 2013-01-28T08:55:44.533 回答
1

如果您正在处理前缀,则不需要正则表达式,您只需要startswith(). 你当然可以使用正则表达式,但它更难阅读和维护,即使是这样一个简单的。startswith()在我看来,更简单。

对于这样一个简单的问题,其他答案似乎太复杂了。我建议使用这样的递归函数:

def split_prefixes (word, prefixes):
    split = [p for p in prefixes if word.startswith(p)]
    if split:
        return split + split_prefixes (word[len(split[0]):], prefixes)
    else:
        return [word]

这是结果:

"overachieve" -> ['over', 'achieve']
"reundo" -> ['re', 'un', 'do']
"reuncoundo" -> ['re', 'un', 'co', 'un', 'do']
"empire" -> ['empire']
于 2013-01-28T09:50:41.627 回答