我有一个字符串:
"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)
这取决于索引元组的升序顺序。可以做得更好吗?
答案 0 :(得分:8)
势在必行和有状态:
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
答案 1 :(得分:5)
您可以使用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'
答案 2 :(得分:2)
使用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
答案 3 :(得分:2)
假设没有重叠,那么你可以按相反的顺序进行重复
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
答案 4 :(得分:1)
这是一个使用字符串格式化和元组解包的快速而略显肮脏的解决方案:
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)
假设您的字符串中没有使用波浪号(~
) - 如果有,请替换为其他字符。这也假定没有任何替换"小组是连续的。
答案 5 :(得分:1)
这是一个反序列表 - 切片分配解决方案:
,
答案 6 :(得分:0)
还有一种解决方案可以完全满足您的需求。
我没有完全解决,但你可能想要使用:
来自re库的re.sub()
。
查看此处,查找函数re.sub()
或re.subn()
:
https://docs.python.org/2/library/re.html
如果我有时间,我会在今天晚些时候制定你的例子。
答案 7 :(得分:0)
又一个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_
答案 8 :(得分:0)
预计如果这些整数对选择在这里很有用,它们在其他地方也会有用,那么我可以这样做:
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]))