【问题标题】:Python: how to replace substrings in a string given list of indicesPython:如何替换给定索引列表的字符串中的子字符串
【发布时间】:2017-07-27 11:12:55
【问题描述】:

我有一个字符串:

"A XYZ B XYZ C"

还有一个索引元组列表:

((2, 5), (8, 11))

我想将索引定义的每个子字符串替换为它们的总和:

A 7 B 19 C

我不能使用字符串替换来做到这一点,因为它会匹配 XYZ 的两个实例。使用索引信息替换将在第二次和第四次迭代中中断,因为索引在整个过程中不断变化。

这个问题有什么好的解决方案吗?

UPDATE. 例如给出字符串。我不知道它的内容,也不能在解决方案中使用它们。

我的肮脏解决方案是:

text = "A XYZ B XYZ C"
replace_list = ((2, 5), (8, 11))

offset = 0
for rpl in replace_list:
    l = rpl[0] + offset
    r = rpl[1] + offset

    replacement = str(r + l)
    text = text[0:l] + replacement + text[r:]

    offset += len(replacement) - (r - l)

这取决于索引元组的升序。可以做得更好吗?

【问题讨论】:

    标签: python string


    【解决方案1】:

    命令式和有状态的:

    s = 'A XYZ B XYZ C'
    indices = ((2, 5), (8, 11))
    res = []
    i = 0
    for start, end in indices:
        res.append(s[i:start] + str(start + end))
        i = end
    res.append(s[end:])
    print(''.join(res))
    

    结果:

    A 7 B 19 C
    

    【讨论】:

    • 这个非常简单整洁
    【解决方案2】:

    你可以使用re.sub():

    In [17]: s = "A XYZ B XYZ C"
    
    In [18]: ind = ((2, 5), (8, 11))
    
    In [19]: inds = map(sum, ind)
    
    In [20]: re.sub(r'XYZ', lambda _: str(next(inds)), s)
    Out[20]: 'A 7 B 19 C'
    

    但请注意,如果匹配的数量大于您的索引对,则会引发StopIteration 错误。在这种情况下,您可以将默认参数传递给 next() 以替换子字符串。

    如果您想使用索引元组来查找子字符串,这里是另一种解决方案:

    In [81]: flat_ind = tuple(i for sub in ind for i in sub)
    # Create all the pairs with respect to your intended indices. 
    In [82]: inds = [(0, ind[0][0]), *zip(flat_ind, flat_ind[1:]), (ind[-1][-1], len(s))]
    # replace the respective slice of the string with sum of indices of they exist in intended pairs, otherwise just the sub-string itself.
    In [85]: ''.join([str(i+j) if (i, j) in ind else s[i:j] for i, j in inds])
    Out[85]: 'A 7 B 19 C'
    

    【讨论】:

    • XYZ 只是一个例子,他们想替换给定范围内的项目。
    • @AshwiniChaudhary 是的,我现在看到了编辑。我会更新答案,谢谢你的注意。
    【解决方案3】:

    使用itertools.groupby 的一种方法。

    from itertools import groupby
    
    
    indices = ((2, 5), (8, 11))
    data = list("A XYZ B XYZ C")
    

    我们首先将匹配项的范围替换为相同数量的None

    for a, b in indices:
        data[a:b] = [None] * (b - a)
    
    print(data)
    # ['A', ' ', None, None, None, ' ', 'B', ' ', None, None, None, ' ', 'C']
    

    我们循环分组数据并将None 组替换为indices 列表中的总和。

    it = iter(indices)
    output = []
    for k, g in groupby(data, lambda x: x is not None):
        if k:
            output.extend(g)
        else:
            output.append(str(sum(next(it))))
    
    print(''.join(output))
    # A 7 B 19 C
    

    【讨论】:

      【解决方案4】:

      这是一个使用字符串格式化和元组解包的快速且略显肮脏的解决方案:

      s = 'A XYZ B XYZ C'
      reps = ((2, 5), (8, 11))
      totals = (sum(r) for r in reps)
      print s.replace('XYZ','{}').format(*totals)
      

      打印出来:

      A 7 B 19 C
      

      首先,我们使用生成器表达式来查找每个替换的总数。然后,通过将'XYZ' 替换为'{}',我们可以使用string formatting - *totals 将确保我们以正确的顺序获得总数。

      编辑

      我没有意识到索引实际上是字符串索引——我的错。为此,我们可以使用re.sub,如下所示:

      import re
      s = 'A XYZ B XYZ C'
      
      reps = ((2, 5), (8, 11))
      for a, b in reps:
          s = s[:a] + '~'*(b-a) + s[b:]
      totals = (sum(r) for r in reps)
      print re.sub(r'(~+)', r'{}', s).format(*totals)
      

      假设您的字符串中没有使用波浪号 (~) - 如果有,请替换为其他字符。这也假设没有一个“替换”组是连续的。

      【讨论】:

      • 这是一个特例。实际上我不知道索引定义了哪些子字符串。 XYZ 只是重复标记的示例。
      • @DenisKulagin 抱歉,我误解了这个问题。让我更新答案
      【解决方案5】:

      假设没有重叠,那么您可以按相反的顺序进行操作

      text = "A XYZ B XYZ C"
      replace_list = ((2, 5), (8, 11))
      
      for start, end in reversed(replace_list):
          text = f'{text[:start]}{start + end}{text[end:]}'
      
      # A 7 B 19 C
      

      【讨论】:

        【解决方案6】:

        这是一个倒序列表切片分配解决方案:

        text = "A XYZ B XYZ C"
        indices = ((2, 5), (8, 11))
        chars = list(text)
        
        for start, end in reversed(indices):
            chars[start:end] = str(start + end)
        
        text = ''.join(chars) # A 7 B 19 C
        

        【讨论】:

          【解决方案7】:

          还有一个解决方案可以完全满足您的需求。 我还没有完全解决,但你可能想使用: re.sub() 来自 re 库。

          看这里,寻找函数re.sub()re.subn()https://docs.python.org/2/library/re.html

          如果我有时间,我会在今天晚些时候完成你的示例。

          【讨论】:

            【解决方案8】:

            又一个itertools 解决方案

            from itertools import *
            
            s = "A XYZ B XYZ C"
            inds = ((2, 5), (8, 11))
            res = 'A 7 B 19 C'
            
            
            inds = list(chain([0], *inds, [len(s)]))
            res_ = ''.join(s[i:j] if k % 2 == 0 else str(i + j)
                    for k, (i,j) in enumerate(zip(inds, inds[1:])))
            
            assert res == res_
            

            【讨论】:

              【解决方案9】:

              预计如果这些整数对选择在这里有用,它们在其他地方也很有用,那么我可能会做这样的事情:

              def make_selections(data, selections):
                  start = 0
                  # sorted(selections) if you don't want to require the caller to provide them in order
                  for selection in selections:
                      yield None, data[start:selection[0]]
                      yield selection, data[selection[0]:selection[1]]
                      start = selection[1]
                  yield None, data[start:]
              
              def replace_selections_with_total(data, selections):
                  return ''.join(
                      str(selection[0] + selection[1]) if selection else value
                      for selection, value in make_selections(data, selections)
                  )
              

              这仍然依赖于不重叠的选择,但我不确定它们重叠意味着什么。

              然后您也可以使替换本身更加灵活:

              def replace_selections(data, selections, replacement):
                  return ''.join(
                      replacement(selection, value) if selection else value
                      for selection, value in make_selections(data, selections)
                  )
              
              def replace_selections_with_total(data, selections):
                  return replace_selections(data, selections, lambda s,_: str(s[0]+s[1]))
              

              【讨论】:

                猜你喜欢
                • 2021-12-19
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2017-06-22
                • 2017-01-14
                • 2021-10-01
                • 1970-01-01
                • 2016-12-15
                相关资源
                最近更新 更多