【问题标题】:Removing symbols between letters Python删除字母Python之间的符号
【发布时间】:2017-10-31 15:04:08
【问题描述】:

我想从字符串中删除某些符号。我只想删除字母之间的符号。如果我的问题不够清楚,那么这里有一些例子: 符号是@31!

输入

@@He11o Wor1d!
!!T3ach !m3
@13!

预期输出

@@Heo Word!
!!Tach !m3
@13!

你能指出我正确的方向吗?我不指望你为我做这件事。我知道这可以通过正则表达式和 for 循环来完成,但对于像我这样的初学者来说,正则表达式似乎有点困难。这是我现在正在做的事情:

string = '@@He11o Wor1d!'
string_copy = string
symbols = "@31!"
for char in symbols:
    string_copy = string_copy.replace(char, "")

我知道这个脚本会替换所有符号

【问题讨论】:

  • 你的输入和输出是什么?请写出来
  • 你为什么不给它加一张支票,然后确保它们跟在后面并跟在一个字母后面?并不是说这是最好的方法,但对你来说明显
  • 我仍然建议将正则表达式与str.replace()结合使用。
  • @DRPK 这是前三行。在左侧的输入和= 之后签署所需的输出
  • 出于好奇,反对票是怎么回事?请解释

标签: python regex string


【解决方案1】:

Regular expressions 一开始肯定很吓人,但值得尝试学习它们,因为它们最终非常有用。在这种情况下,您想要的是:

import re
string = re.sub(r'([a-zA-Z])[@31!]+(?=[a-zA-Z])', r'\1', string)

Let's look at what this does.

re.sub 类似于str.replace,但它使用正则表达式。

[a-zA-Z] 匹配任何字母。

[@31!]+ 匹配一个或多个列出的符号。

+ 导致生成的 RE 匹配前一个 RE 的 1 次或多次重复。

(?=[a-zA-Z]) 是一个字母的前瞻断言。这意味着匹配后跟一个字母,但该字母不是匹配的一部分。

(?=...) 匹配 if ... 匹配下一个,但不消耗任何字符串。这称为前瞻断言。例如,Isaac (?=Asimov) 仅在 'Asimov' 后跟 'Isaac' 时才会匹配。

所以([a-zA-Z])[@31!]+(?=[a-zA-Z]) 匹配一个字母后跟列表中的一个或多个符号。此匹配后跟一个字母,但匹配不包括该字母。

\1 是对正则表达式中括号组的反向引用,在本例中为 [a-zA-Z]。这就是我们想要替换我们找到的内容。

(字符串前的rs 是为了使它们成为原始字符串,这在使用正则表达式时通常会有所帮助。)

编辑:

正如@ctwheels 指出的那样,you can also use a lookbehind assertion rather than a backreference

string = re.sub(r'(?<=[a-zA-Z])[@31!]+(?=[a-zA-Z])', r'', string)

【讨论】:

  • 您可以添加指向 regex101 的链接
  • 非常感谢,这个信息也很有用
  • @AnttiHaapala 添加了链接
【解决方案2】:

正确地做到这一点很棘手。虽然我通常更喜欢避免使用正则表达式,除非它们是必要的,但这绝对是它们使工作变得更容易的情况。很多。但无论如何,这是一个非正则表达式解决方案。

我们使用标准的groupby 函数将输入字符串分成三种组:“A”组包含字母,“S”组包含特殊符号,“O”组包含其他任何内容。然后我们扫描这些组,将它们复制到result 列表中,除非该组是一个“S”组并且它的前面和后面都有一个“A”组。最后,我们将复制的组重新连接成一个字符串。

为了更容易检查以下组,我们将('O', '') 的“假”组添加到组列表的末尾。这样一来,每个真实组都有一个后续组。

from itertools import groupby

symbols = '@31!'

def keyfunc(c):
    if c in symbols:
        return 'S'
    elif c.isalpha():
        return 'A'
    else:
        return 'O'

def remove_symbols(s):
    groups = [(k, ''.join(g)) for k, g in groupby(s, keyfunc)] + [('O', '')]
    result = []
    prev = 'O'
    for i, (k, g) in enumerate(groups[:-1]):
        # If a group of symbols has an alpha group on both sides, don't copy it
        if not (k == 'S' and prev == 'A' and groups[i+1][0] == 'A'):
            result.append(g)
        prev = k
    return ''.join(result)

# Test

data = '''\
@@He11o Wor1d!
!!T3ach !m3
@13!
lala@@@@ 
'''

expected = '''\
@@Heo Word!
!!Tach !m3
@13!
lala@@@@
'''
print('Data')
print(data)

print('Expected')
print(expected)

print('Output')
for s in data.splitlines():
    print(remove_symbols(s))   

输出

Data
@@He11o Wor1d!
!!T3ach !m3
@13!
lala@@@@ 

Expected
@@Heo Word!
!!Tach !m3
@13!
lala@@@@

Output
@@Heo Word!
!!Tach !m3
@13!
lala@@@@ 

【讨论】:

  • 我不得不承认这看起来有点棘手,但如果它有效的话! :) 我会尝试解决这个问题,看看我是否能让自己理解一切。谢谢
  • @Innit2 很高兴。我坦率地承认这是相当高级的代码,但它要正确解决的棘手问题。我可以编写一个不使用groupby 的解决方案,但它会更长并且(恕我直言)更难遵循。 groupbyenumerate 起初有点吓人,但它们非常有用,绝对值得一玩并适应它们。与正则表达式相比,学习曲线更少。 :D
  • 这是表明您不要在没有正则表达式的情况下执行这些操作的最佳答案。它变得太复杂了。
  • @Innit2 所以最后学习阅读正则表达式语法更容易 - 它仍然可以更容易看到它的作用 - 另一方面,这个非常即使是经验丰富的 Python 开发人员,一开始也很难理解。
【解决方案3】:

代码

See this regex in use here

(?<=[a-z])[@13!]+(?=[a-z])

结果

输入

@@He11o Wor1d!
!!T3ach !m3
@13!

输出

@@Heo Word!
!!Tach !m3
@13!

说明

  • (?&lt;=[a-z]) 正向后视确保前面是az 之间的字母
  • [@13!]+ 匹配集合中存在的一个或多个字符 @13!
  • (?=[a-z]) 正向前瞻确保后面是az 之间的字母

使用i 标志使模式不区分大小写,因此a-z 也匹配A-Z


用法

import re
regex = r"(?<=[a-z])[@13!]+(?=[a-z])"
result = re.sub(regex, "", string, 0, re.IGNORECASE)
# re.IGNORECASE can be replaced with the shortened re.I

(正则表达式中的标志,而不是传递给函数)

import re
regex = r"(?i)(?<=[a-z])[@13!]+(?=[a-z])"
result = re.sub(regex, "", string)

【讨论】:

  • 感谢您的建议!
  • 顺便说一句,您的链接是指向不同版本的正则表达式,是故意的吗?
  • 你也可以在flags中添加i
  • @AnttiHaapala 第二版链接默认为python。
  • @AnttiHaapala 我在使用中添加了一个新部分。你指的是这个吗?我还在第一个用法下添加了评论。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-09
相关资源
最近更新 更多