在任意索引处有效地分区字符串

时间:2013-12-20 04:04:41

标签: python string python-3.x idioms

给定任意字符串(即,不基于模式),请说:

>>> string.ascii_letters
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'

我正在尝试根据索引列表对字符串进行分区。

这是我尝试过的,它确实有效:

import string

def split_at_idx(txt, idx):
    new_li=[None]*2*len(idx)
    new_li[0::2]=idx
    new_li[1::2]=[e for e in idx]
    new_li=[0]+new_li+[len(txt)]
    new_li=[new_li[i:i+2] for i in range(0,len(new_li),2)]  
    print(new_li)
    return [txt[st:end] for st, end in new_li]

print(split_at_idx(string.ascii_letters, [3,10,12,40]))  
# ['abc', 'defghij', 'kl', 'mnopqrstuvwxyzABCDEFGHIJKLMN', 'OPQRSTUVWXYZ']

拆分基于索引列表[3,10,12,40]。然后需要将此列表转换为开始,结束对列表,如[[0, 3], [3, 10], [10, 12], [12, 40], [40, 52]]。我使用切片分配来设置平均值和赔率,然后将列表理解分组成对,并使用第二个LC来返回分区。

这对于这样一个简单的功能来说似乎有点复杂。有没有更好/更有效/更惯用的方式来做到这一点?

2 个答案:

答案 0 :(得分:8)

我觉得最近有人问过这个问题,但我现在找不到了。假设丢失的信件是意外,你不能这样做:

def split_at_idx(s, idx):
    return [s[i:j] for i,j in zip([0]+idx, idx+[None])]

之后我们

>>> split_at_idx(string.ascii_letters, [3, 10, 12, 40])
['abc', 'defghij', 'kl', 'mnopqrstuvwxyzABCDEFGHIJKLMN', 'OPQRSTUVWXYZ']
>>> split_at_idx(string.ascii_letters, [])
['abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ']
>>> split_at_idx(string.ascii_letters, [13, 26, 39])
['abcdefghijklm', 'nopqrstuvwxyz', 'ABCDEFGHIJKLM', 'NOPQRSTUVWXYZ']

答案 1 :(得分:1)

这似乎是itertools.groupby的工作。

def split_at_indices(text, indices):
    [''.join(e[1] for e in g) for k,g in groupby(
      enumerate(text), key=lambda x: bisect_right(indices, x[0])
     )]

您需要从bisect_right模块中导入bisect

这可以按照您认为有效实现的方式运行:对于字符串中的每个字符,它使用indices中的二进制搜索来计算一个数字,表示字符应该进入的最终列表中的哪个字符串,然后groupby用这些数字分隔字符。虽然在大多数情况下效率较低,但因为数组访问速度非常快。