【问题标题】:Python regex: replace numbers and special characters except yearsPython regex:替换数字和特殊字符,但年份除外
【发布时间】:2017-09-07 07:55:34
【问题描述】:

我想用空格替换所有非字母字符,不包括 1950 年到 2029 年之间的年份。 例如:

ab-c 0123 4r. a2017 2010 -> ab c r a 2010

到目前为止我的尝试,试图通过否定的前瞻将日期列入黑名单:

re.sub('(?!\b19[5-9][0-9]\b|\b20[0-2][0-9]\b)([^A-Za-z]+)', ' ', string)

由于这不起作用,非常感谢任何帮助!

【问题讨论】:

  • 但是你已经替换了2017

标签: python regex


【解决方案1】:

您可以使用一个简单的正则表达式并传递一个函数来检查它是否是一年:

import re

def replace_non_year_numbers(m):
  number = int(m.group(0))
  if 1950 <= number <= 2029:
    return str(number)
  else:
    return ''

print(re.sub('\d+', replace_non_year_numbers, 'ab-c 0123 4r. a2017 2010'))
# 'ab-c  r. a2017 2010'

为了保持正则表达式和逻辑简单,您可以在第二步中删除特殊字符:

only_years = re.sub('\d+', replace_non_year_numbers, 'ab-c 0123 4r. a2017 2010')
no_special_char = re.sub('[^A-Za-z0-9 ]', ' ', only_years)
print(re.sub(' +', ' ', no_special_char))
# ab c r a2017 2010

【讨论】:

  • 您的结果中仍有-.
  • @Kasramvd:我在最后一句中提到了。
【解决方案2】:

让我们选择要在结果中保留的内容。查看正则表达式:

(
  (?<!\w)                       # neg. lookbehind: not a word char
  (1                            # read a '1'
     (?=9[5-9][0-9])            # lookahead: following 3 digits make it
                                #   a year between 1950 and 1999
     [0-9]{3}                   # THEN read these 3 digits
   |                            # - OR -
   2                            # read a '2'
     (?=0[0-2][0-9])            # lookahead: following 3 digits make it
                                #   a year between 2000 and 2029
     [0-9]{3}                   # THEN read these 3 digits 
  )
  |                             # - OR -
  [a-zA-Z]                      # read some letter
)+

在单列中:

((?<!\w)(1(?=9[5-9][0-9])[0-9]{3}|2(?=0[0-2][0-9])[0-9]{3})|[a-zA-Z])+

你可以在regex 101上测试它

让我们把它放在一个 python 脚本中:

$ cat test.py
import re

pattern = r"(?:(?<!\w)(?:1(?=9[5-9][0-9])[0-9]{3}|2(?=0[0-2][0-9])[0-9]{3})|[a-zA-Z])+"

tests = ["ab-c 0123 4r. a2017 2010 a1955 1955 abc"]

for elt in tests:
   matches = re.findall(pattern, elt)
   print ' '.join(matches)

给出:

$ python test.py
ab c r a 2010 a 1955 abc

【讨论】:

    【解决方案3】:

    不太漂亮,但我会使用多个替换:

    import re
    
    def check_if_year(m):
      number = int(m.group(0))
      if 1950 <= number <= 2029:
        return str(number)
      else:
        return ' '
    
    s = 'ab-c 0123 4r. a2017 2010 1800'             # Added 1800 for testing
    print(s)
    print('ab c r a 2010')
    t = re.sub(r'[^A-Za-z0-9 ]+', ' ', s)           # Only non-alphanumeric
    t = re.sub(r'(?!\b\d{4}\b)(?<!\d)\d+', ' ', t)  # Only numbers that aren't standalone 4 digits
    t = re.sub(r'\d+', check_if_year, t)            # Only standalone 4 digits number and test for year
    t = re.sub(r' {2,}', ' ', t).strip()            # Clean up extra spaces
    print(t)
    

    ideone demo

    (?!\b\d{4}\b)(?<!\d)\d+
    

    将匹配任何数字,只要它不是“独立”的 4 位数字(除空格或字符串开头/结尾外没有字符),并且我正在使用 (?&lt;!\d) 以便它不会尝试匹配在一个数字的中间。

    【讨论】:

    • 与我的回答相比有什么附加价值?你只是复制粘贴函数吗?
    • @EricDuminil 2017 年被替换,而 2010 年仍然存在
    • @EricDuminil 是的,它基本上是一个副本。没有很多方法可以简单地检查一个数字是否在某个范围内^^;
    猜你喜欢
    • 2021-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-17
    • 2015-07-10
    • 2014-07-13
    • 1970-01-01
    • 2012-04-06
    相关资源
    最近更新 更多