在Python中拆分字符串的最有效方法

mal*_*ave 26 python optimization split

我当前的Python项目需要大量的字符串拆分来处理传入的包.由于我将在一个非常慢的系统上运行它,我想知道最有效的方法是什么.字符串的格式如下:

Item 1 | Item 2 | Item 3 <> Item 4 <> Item 5
Run Code Online (Sandbox Code Playgroud)

说明:此特定示例来自列表,其中前两个项目是标题和日期,而项目3到项目5将被邀请人员(其数量可以是从零到n的任何值,其中n是数字服务器上的注册用户).

从我看到的,我有以下选择:

  1. 反复使用 split()
  2. 使用正则表达式和正则表达式函数
  3. 我还没有想到的其他一些Python函数(可能还有一些)

解决方案1将包括拆分,|然后拆分结果列表的最后一个元素<>为此示例,而解决方案2可能会导致正则表达式,如:

((.+)|)+((.+)(<>)?)+

好的,这个RegEx太可怕了,我可以看到自己.它也是未经测试的.但是你明白了.

现在,我正在寻找a)花费最少时间和b)理想地使用最少量内存的方式.如果只有两个中的一个是可能的,我宁愿更少的时间.理想的解决方案也适用于具有更多项目|和完全没有的字符串的字符串<>.至少基于正则表达式的解决方案会这样做

我的理解是split()会使用更多的内存(因为你基本上得到两个结果列表,一个分裂,|第二个分裂<>),但我不太了解Pythons实现常规表达式来判断RegEx如何执行.split()如果它与某些不同数量的项目和第二个分隔符的缺失有关,那么它也不如正则表达式动态.尽管如此,我还是不能动摇python在没有正则表达式的情况下可以做得更好的印象,这就是我要问的原因

一些说明:

  • 是的,我可以对这两个解决方案进行基准测试,但我正在尝试学习一般的python以及它如何在这里工作,如果我只是对这两个进行基准测试,我仍然不知道我错过了什么python函数.
  • 是的,在这个级别进行优化只是高性能的东西才真正需要,但正如我所说,我正在尝试学习有关python的东西.
  • 另外:在最初的问题中,我完全忘记提到我需要能够区分分离|的部分和分隔符的部分<>,因此re.split(\||<>,input)(由@obmarg提出)生成的简单平面列表不会工作得太好了.适合这个标准的解决方案非常受欢迎.

总结一下这个问题:出于什么原因,哪种解决方案最有效.

由于多个请求,我在split()@smarg 上运行了-solution和第一个提出的正则表达式的时间,以及@mgibsonbr和@duncan的解决方案:

import timeit
import re

def splitit(input):
    res0 = input.split("|")
    res = []
    for element in res0:
        t = element.split("<>")
        if t != [element]:
            res0.remove(element)
            res.append(t)
    return (res0, res)

def regexit(input):
    return re.split( "\||<>", input )


def mgibsonbr(input): # Solution by @mgibsonbr
    items = re.split(r'\||<>', input) # Split input in items
    offset = 0
    result = [] # The result: strings for regular itens, lists for <> separated ones
    acc = None
    for i in items:
        delimiter = '|' if offset+len(i) < len(input) and input[offset+len(i)] == '|' else '<>'
        offset += len(i) + len(delimiter)
        if delimiter == '<>': # Will always put the item in a list
            if acc is None:
                acc = [i] # Create one if doesn't exist
                result.append(acc)
            else:
                acc.append(i)
        else:
            if acc is not None: # If there was a list, put the last item in it
                acc.append(i)
            else:
                result.append(i) # Add the regular items
            acc = None # Clear the list, since what will come next is a regular item or a new list
    return result

def split2(input): # Solution by @duncan
    res0 = input.split("|")
    res1, res2 = [], []
    for r in res0:
        if "<>" in r:
            res2.append(r.split("<>"))
        else:
            res1.append(r)
    return res1, res2

print "mgibs:", timeit.Timer("mgibsonbr('a|b|c|de|f<>ge<>ah')","from __main__ import mgibsonbr").timeit()
print "split:", timeit.Timer("splitit('a|b|c|de|f<>ge<>ah')","from __main__ import splitit").timeit()
print "split2:", timeit.Timer("split2('a|b|c|de|f<>ge<>ah')","from __main__ import split2").timeit()
print "regex:", timeit.Timer("regexit('a|b|c|de|f<>ge<>ah')","from __main__ import regexit").timeit()
print "mgibs:", timeit.Timer("mgibsonbr('a|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>ah')","from __main__ import mgibsonbr").timeit()
print "split:", timeit.Timer("splitit('a|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>ah')","from __main__ import splitit").timeit()
print "split:", timeit.Timer("split2('a|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>ah')","from __main__ import split2").timeit()
print "regex:", timeit.Timer("regexit('a|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>ah')","from __main__ import regexit").timeit()
Run Code Online (Sandbox Code Playgroud)

结果:

mgibs: 14.7349407408
split: 6.403942732
split2: 3.68306812233
regex: 5.28414318792
mgibs: 107.046683735
split: 46.0844590775
split2: 26.5595985591
regex: 28.6513302646
Run Code Online (Sandbox Code Playgroud)

目前看起来像@duncan的split2击败了所有其他算法,无论长度如何(至少使用这个有限的数据集),看起来@ mgibsonbr的解决方案也有一些性能问题(抱歉'但是,谢谢你无论解决方案).

感谢大家的意见和建议.

Dun*_*can 18

split()对你的代码表现如此糟糕感到有些惊讶,所以我仔细看了一下,注意到你正在调用list.remove()内循环.你还要split()在每个字符串上调用额外的时间.摆脱那些和解决方案使用split()正则表达式击败较短的字符串并在较长的字符串上相当接近.

import timeit
import re

def splitit(input):
    res0 = input.split("|")
    res = []
    for element in res0:
        t = element.split("<>")
        if t != [element]:
            res0.remove(element)
            res.append(t)
    return (res0, res)

def split2(input):
    res0 = input.split("|")
    res1, res2 = [], []
    for r in res0:
        if "<>" in r:
            res2.append(r.split("<>"))
        else:
            res1.append(r)
    return res1, res2

def regexit(input):
    return re.split( "\||<>", input )

rSplitter = re.compile("\||<>")

def regexit2(input):
    return rSplitter.split(input)

print("split:", timeit.Timer("splitit('a|b|c|de|f<>ge<>ah')","from __main__ import splitit").timeit())
print("split2:", timeit.Timer("split2('a|b|c|de|f<>ge<>ah')","from __main__ import split2").timeit())
print("regex:", timeit.Timer("regexit('a|b|c|de|f<>ge<>ah')","from __main__ import regexit").timeit())
print("regex2:", timeit.Timer("regexit2('a|b|c|de|f<>ge<>ah')","from __main__ import regexit2").timeit())
print("split:", timeit.Timer("splitit('a|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>ah')","from __main__ import splitit").timeit())
print("split2:", timeit.Timer("split2('a|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>ah')","from __main__ import split2").timeit())
print("regex:", timeit.Timer("regexit('a|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>ah')","from __main__ import regexit").timeit())
print("regex2:", timeit.Timer("regexit2('a|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>aha|b|c|de|f<>ge<>ah')","from __main__ import regexit2").timeit())
Run Code Online (Sandbox Code Playgroud)

这给出了以下结果:

split: 1.8427431439631619
split2: 1.0897291360306554
regex: 1.6694280610536225
regex2: 1.2277749050408602
split: 14.356198082969058
split2: 8.009285948995966
regex: 9.526430513011292
regex2: 9.083608677960001
Run Code Online (Sandbox Code Playgroud)

当然,split2()给出了你想要的嵌套列表,而正则表达式解决方案没有.

编辑:我已经更新了这个答案,包括@ F1Rumors建议编译正则表达式将提高性能.它确实略有不同,但Python缓存编译的正则表达式,因此节省的数量并不像您预期​​的那么多.我认为通常不值得为速度做(尽管在某些情况下可能会这样),但通常值得让代码更清晰.

我还更新了代码,因此它在Python 3上运行.


obm*_*arg 10

我不确定它是否是最有效的,但肯定最简单的代码似乎是这样的:

>>> input = "Item 1 | Item 2 | Item 3 <> Item 4 <> Item 5"
>>> re.split( "\||<>", input )
>>> ['Item 1 ', ' Item 2 ', ' Item 3 ', ' Item 4 ', ' Item 5']
Run Code Online (Sandbox Code Playgroud)

我认为它有可能比普通的旧分裂更有效(取决于输入数据),因为你需要对第一次分割的每个字符串输出执行第二次分割操作,这不是似乎对记忆或时间都有效.

虽然说过我可能很容易出错,但唯一能确定的方法就是计时.