【问题标题】:How to do conditional character replacement within a string如何在字符串中进行条件字符替换
【发布时间】:2010-12-23 09:07:31
【问题描述】:

我在 Python 中有一个 unicode 字符串,基本上需要逐个字符地遍历并根据规则列表替换某些字符串。一条这样的规则是,如果 an 之后,则将 a 更改为 ö。此外,如果连续有两个元音字符,它们将被一个元音字符和: 替换。所以如果我有字符串"natarook",那么获取"nötaro:k" 的最简单和最有效的方法是什么?如果重要,请使用 Python 2.6 和 CherryPy 3.1。

编辑:连续的两个元音确实意味着相同的元音(oo、aa、ii)

【问题讨论】:

  • 您需要更明确地说明“连续两个元音字符”规则——假设它适用于“book”,但不适用于“bear”。
  • 这就像拔牙——如果两个元音字符不需要相等,替换中出现哪一个??
  • 是的,我的意思是连续两个双元音 (ii, aa, oo)
  • “两个双元音”实际上就像“aaoo”。 “连续”意味着什么约束?

标签: python string replace conditional


【解决方案1】:

鉴于您的规则,我想说您真的想要一个简单的状态机。嗯,再想一想,也许不是;您可以随时回顾字符串。

我在 Python 中有一个 unicode 字符串,基本上需要逐个字符地遍历并根据规则列表替换某些字符串。一个这样的规则是,如果 a 在 n 之后,则将 a 更改为 ö。此外,如果连续有两个元音字符,它们将被一个元音字符和 : 替换。因此,如果我有字符串,获取“nötaro:k”的最简单和最有效的方法是什么?如果重要,请使用 Python 2.6 和 CherryPy 3.1。

vowel_set = frozenset(['a', 'e', 'i', 'o', 'u', 'ö'])

def fix_the_string(s):
    lst = []
    for i, ch in enumerate(s):
        if ch == 'a' and lst and lst[-1] == 'n':
            lst.append('ö')
        else if ch in vowel_set and lst and lst[-1] in vowel_set:
            lst[-1] = 'a' # "replaced by one vowel character", not sure what you want
            lst.append(':')
        else
            lst.append(ch)
    return "".join(lst)

print fix_the_string("natarook")

编辑:现在我看到了@Anon 的答案。我认为这是最简单的方法。一旦你掌握了一大堆规则,这实际上可能会更快,因为它会越过字符串;但也许不是,因为 Python 中的正则表达式是快速的 C 代码。

但越简单越好。这是正则表达式方法的实际 Python 代码:

import re
pat_na = re.compile(r'na')
pat_double_vowel = re.compile(r'([aeiou])[aeiou]')

def fix_the_string(s):
    s = re.sub(pat_na, r'nö', s)
    s = re.sub(pat_double_vowel, r'\1:', s)
    return s

print fix_the_string("natarook") # prints "nötaro:k"

【讨论】:

  • lst 不能只是一个字符串吗?
  • 我们可以使用字符串代替 lst。但是字符串在 Python 中是不可变的,我们不能只替换最后一个字符;我们将不得不做类似s_new = s_new[:-1] + new_ch 的事情。真正的问题是性能真的很糟糕。追加到列表或替换元素是一种快速操作,但追加到字符串涉及将字符串复制到新字符串,然后添加新字符;这给出了 O(N**2) 的性能,非常糟糕。最新版本的 Python 至少在某些时候优化了这种特定情况,但这是传统的实现方式。
  • @steveha:注意,您正在测试最后一个输出字符 (lst[-1]) 而不是前一个输入字符 - 取决于 OP 的全套规则中的其他内容,这可能是个好主意或坏主意。
  • @steveha:你说得有道理,确实在问题描述中没有说它是一个小字符串。不错的re 替代解决方案。
【解决方案2】:

“我知道,我会使用正则表达式!”

但说真的,正则表达式非常适合字符串操作。

您可以为每个规则编写一个,如下所示:

s/na/nö/g
s/([aeiou])$1/$1:/g

或者您可以在运行时从列出所有这些的其他来源生成它们。

【讨论】:

  • 在 Python 中从未见过这种语法,您使用的是哪个模块?
  • 该语法是 vi 文本编辑器的典型语法;它表达了这个想法,但它不适用于 Python 代码。
  • 这是正则表达式的 Perl 语法 - 用反斜杠替换 $ 符号以获得 Python 语法。
  • 当然,这只是示例表达式 - 实际执行替换所需的样板 Python 是额外的。
  • 我的错误。 vi 不会使用$1 替换文本;它使用\1。我在那里迷惑了一会儿。
【解决方案3】:

首先关注简单正确,如果分析表明它是瓶颈,则考虑效率。

简单的方法是:

prev = None
for ch in string:
  if ch == 'a':
    if prev == 'n':
      ...
  prev = ch

【讨论】:

    【解决方案4】:
    # -*- coding: utf-8 -*-
    
    def subpairs(s, prefix, suffix):
        def sub(i, sentinal=object()):
            r = prefix.get(s[i:i+2], sentinal)
            if r is not sentinal: return r
    
            r = suffix.get(s[i-1:i+1], sentinal)
            if r is not sentinal: return r
            return s[i]
    
        s = '\0'+s+'\0'
        return ''.join(sub(i) for i in xrange(1,len(s)))
    
    vowels = [(v+v, u':') for v in 'aeiou']
    
    prefix = {}
    suffix = {'na':u'ö'}
    suffix.update(vowels)
    print subpairs('natarook', prefix, suffix)
    # prints: nötaro:k
    
    prefix = {'na':u'ö'}
    suffix = dict(vowels)
    print subpairs('natarook', prefix, suffix)
    # prints: öataro:k
    

    【讨论】:

    • 这是一个原创的想法,但也许你想向 OP 解释一下 ;-)
    • 说得好! Dispatch 是(前一个+当前)字符对的表。 Subpairs 使用 \0 对字符串进行成对迭代以表示字符串开始。在调度表中查找每一对以供替换使用。如果没有找到,get 返回当前字符“c”。算法是线性复杂度 - O(n)。
    • 另外,我怎么能在规则之前做呢?所以对于 'na':u'ö',将 'n' 改为 'ö' 而不是 'a'
    • 好的,我合并了前缀和后缀映射,以及固定元音序列到 OP 的意图。算法仍然是 O(n) 复杂度。请注意,内部 sub() 方法可以替换为算法的组合。
    【解决方案5】:

    使用手工制作的正则表达式列表可能更简单,而不是通过程序生成它们。我推荐以下代码。

    import re
    # regsubs is a dictionary of regular expressions as keys, 
    # and the replacement regexps as values
    regsubs = {'na':u'nö',
               '([aeiou])\\1': '\\1:'}
    
    def makesubs(s):
        for pattern, repl in regsubs.iteritems():
            s = re.sub(pattern, repl, s)
        return s
    
    print makesubs('natarook')
    # prints: nötaro:k
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-10-08
      • 1970-01-01
      • 1970-01-01
      • 2011-07-25
      • 2021-06-19
      • 2014-03-13
      相关资源
      最近更新 更多