Python:通过索引删除子字符串

时间:2015-12-07 22:27:38

标签: python string

我有以下相当简单的代码段:

def delete_substring_blocks(s, blocks):                                                                             
  '''                                                                                                                   
      s: original input string                                                                                   
      blocks: list of indices (start, end) to be deleted                                                                

      return string `out` where blocks are deleted from s                                                      
  '''                                                                                                                   
  out = ''                                                                                                              
  p = 0                                                                                                                 
  for start, end in blocks:                                                                                             
      out += s[p:start]                                                                                               
      p = end                                                                                                           
  out += s[p:]                                                                                                        
  return out

此函数采用字符串s并删除s[start:end]中的所有s,其中索引(start, end)对在列表blocks中给出。

是否有某个内置函数可以执行相同的操作?

更新: 我的代码中有一个假设:

  1. 块按第一个索引按升序排序(由list.sort()就地完成)
  2. 至于块是否可以重叠,在我的用例中我确保它们在调用函数之前不会。但为了好玩,我们也可以假设他们这样做。

5 个答案:

答案 0 :(得分:3)

我的方法将blocks转换为一组我称之为exclude的索引。之后,循环遍历字符串并排除索引在exclude集中的那些字符。我使用set而不是list,因为它很好地处理重复项(如果重叠范围)。

构建exclude

给定一个无序的,可能重叠的范围列表:

blocks = [(5, 7), (2, 4), (6, 10)]

我想将其转换为:

exclude = {2, 3, 5, 6, 7, 8, 9}

如何:

exclude = set()
for block in blocks:
    exclude.update(range(*block))

全部放在一起

这是我的代码和最后的一个小例子。请注意,我选择重命名该函数,因为此函数足够通用,可以处理字符串,列表,元组和其他可迭代对象,而不仅仅是字符串。另外,因为函数返回一个列表,所以当处理字符串时,我们需要将字符列表重新加入。

def delete_blocks(iterable, blocks):                                                                             
    exclude = set()
    for block in blocks:
        exclude.update(range(*block))
    return [cell for i, cell in enumerate(iterable) if i not in exclude]

# Try it out
test_string = '0123456789abc'
blocks = [(5, 7), (2, 4), (6, 10)]
result = ''.join(delete_blocks(test_string, blocks))

print('Before: {!r}'.format(test_string))
print('Blocks:', blocks)
print('After: {!r}'.format(result))

更新:实施delete_substring_blocks

为了真正回答Mai的问题,我使用delete_substring_blocks实施了delete_blocks

def delete_substring_blocks(s, blocks):
    return ''.join(delete_blocks(s, blocks))

答案 1 :(得分:0)

您需要按反向排序顺序处理块,否则字符串的预期部分将更改位置,块索引将无效。

这将是:

def delete_substring_blocks(s, blocks):
  '''
      s: original input string
      blocks: list of indices (start, end) to be deleted

      return string `out` where blocks are deleted from orig_str
  '''
  for start, end in reversed(sorted(blocks)):
    s = s[:start] + s[end:]
  return s

答案 2 :(得分:0)

由于未指明,我们必须假设块列表可能包含重叠。

一个效率相对较低的表达式,但是处理重叠和非排序块的表达式是:

def delete_substring_blocks(s, blocks):
    return ''.join(
        [c for i, c in enumerate(s) 
         if not any(blk for blk in blocks 
                    if i >= blk[0] and i < blk[1])])

在这里,我们只测试每个角色的位置,看它是否在任何一个区间内,如果不是,则接受它。

以下是重叠块的示例:

>>> delete_substring_blocks(
        "hello there how are you", 
        [[0, 3], [7, 9], [7, 10]])
'lo te how are you'

由于你似乎发现这个表达不可读,所以在这里分解一下:

def delete_substring_blocks(s, blocks):
    def check_pos(i):
        return not any(1 for start, end in blocks 
                       if i >= start and i < end)

    return ''.join([c for i, c in enumerate(s) 
                    if check_pos(i)])

答案 3 :(得分:0)

这是一个基于位图的解决方案。它可以处理重叠块:

def delete_substring_blocks(s, blocks):
    # create a bitmap with False for characters to be deleted
    preserve = [True] * len(s)
    for i, j in blocks:
        preserve[i:j] = False

    result = []
    for i, c in enumerate(s):
        if preserve[i]:
            result.append(c)

    return ''.join(result)

答案 4 :(得分:-1)

没有。你要求的是相当具体的。如果你想指定你想要保留的字符串部分(而不是删除),你可以很容易地将它排成一行。

>>> string = 'my long string'
>>> ''.join([string[s:e] for s, e in [(0,3), (8, 14)]])
'my string'