我想替换 letters/words,但我在代码的一个方面面临挑战

I want to replace letters/words, but I am facing challenges in one aspect of my code

我将使用 lloll 作为示例词。

这是我的代码:

mapping = {'ll':'o','o':'ll'}
string = 'lloll'
out = ' '.join(mapping.get(s,s) for s in string.split())
print(out)

输出应该是 ollo,但我得到 lloll。当我写 ll o ll 时,它可以工作,但我不想在 llo 之间有空格,而且我不想做类似 mapping = {'lloll':'ollo'} 的事情.

不确定这是否考虑了所有边缘情况(重叠匹配如何?),但我目前的想法是 re.split 通过映射键的字符串,然后应用映射。

import re
mapping = {'ll':'o','o':'ll'}
string = 'lloll'
choices = f'({"|".join(mapping)})'
result = ''.join(mapping.get(s, s) for s in re.split(choices, string))

使用Template.substitute

我会发现每个出现的 sub-strings 都会被替换,并且 pre-fix 它们会被替换为 $。然后使用 string.Template 中的 substitute(mapping) 有效地进行全局查找并替换它们。

在这种方法中最重要的是,您可以通过使用带有排序键的 sorted()reversed() 来控制 可能重叠的映射 的处理方式它们的应用顺序。

findall然后split_string

通过这种方式,您还可以获得一些不错的额外生成器函数,用于 findall 个子字符串的出现以及 split_string 到给定索引处的段,这可能有助于您执行任何更大的任务。如果它们没有价值,底部有较短的版本。

鉴于它在整个过程中都使用了生成器,因此它应该非常快且内存效率高。

from itertools import chain, repeat
from string import Template


def CharReplace(string: str, map):
    if ("$" in map) or ("$" in string):
        raise ValueError("Cannot handle anything with a $ sign")
    
    for old in map: #"old" is the key in the mapping
        locations = findall(string, old) #index of each occurance of "old"
        bits = split_string(string, locations) #the string split into segments, each starting with "old"
        template = zip(bits, repeat("$")) #tuples: (segment of the string, "$")
        string = ''.join(chain(*template))[:-1] #use chain(*) to unpack template so we get a new string with a $ in front of each occurance of "old" and strip the last "$" from the end

    template = Template(string)
    string = template.substitute(map) #substitute replaces substrings which follow "$" based on a mapping

    return string


def findall(string: str, sub: str):
    i = string.find(sub)
    while i != -1:
        yield i
        i = string.find(sub, i + len(sub))

def split_string(string: str, indices):
    for i in indices:
        yield string[:i]
        string = string[i:]
    yield string

如果没有一些额外的代码来转义它们,这种方法将不会处理任何带有“$”的字符串。

它将 运行 从前到后遍历字符串,一次一个键,无论字典以何种顺序迭代它们。您可以在行 for old in map 中的键上添加某种形式的 sorted() 以便按特定顺序处理键(例如最长的第一个,按字母顺序)。

它将处理重复出现的键,这样 llll 将被识别为 ll ll 并且 lll 将被识别为 ll l

在你原来的情况下,这首先将原始字符串变成 $llo$ll 然后变成 $ll$o$ll 然后使用 substitute 得到 ollo

单个生成器添加分隔符

如果您希望代码更短:

def CharReplace2(string: str, map):
    if ("$" in map) or ("$" in string):
        raise ValueError("Cannot handle anything with a $ sign")
    
    for old in map: #"old" is the key in the mapping
        string = ''.join(add_delimiters(string, old)) #add a $ before each occurrence of old
    template = Template(string)
    string = template.substitute(map) #substitute replaces substrings which follow "$" based on a mapping

    return string

def add_delimiters(string: str, sub: str):
    i = string.find(sub)
    while i != -1:
        yield string[:i] #string up to next occurrence of sub
        string = ''.join(('$',string[i:])) #add a dollar to the start of the rest of the string (starts with sub)
        i = string.find(sub, i + len(sub)) #and find the next occurrence
    yield(string) #don't forget to yield the last bit of the string