【问题标题】:Python: Find and replace under certain conditions with regexPython:在某些条件下使用正则表达式查找和替换
【发布时间】:2015-12-04 16:51:46
【问题描述】:

基本上我想编写一个脚本来清理 URL,将点替换为“(点)”字符串。 例如,如果我在运行脚本后有http://www.google.com,我希望它是http://www(dot)google(dot)。 好吧,当我的文本文件仅包含 url 或其他字符串时,使用 .replace 很容易实现,但在我的情况下,我的文本文件中也有 IP 地址,我不希望 IP 地址中的点更改为“ (点)”。

我尝试使用正则表达式来执行此操作,但我的输出是 " http://ww(dot)oogl(dot)om 192.60.10.10 33.44.55.66"

这是我的代码

from __future__ import print_function


import sys
import re

nargs = len(sys.argv)
if nargs < 2:

    sys.exit('You did not specify a file')
else:
    inputFile = sys.argv[1]
    fp = open(inputFile)
    content = fp.read()

replace = '(dot)'
regex = '[a-z](\.)[a-z]'
print(re.sub(regex, replace, content, re.M| re.I| re.DOTALL))

我想我需要有一个条件来检查模式是否为 number.number - 不要替换。

【问题讨论】:

    标签: python regex


    【解决方案1】:

    您可以使用前瞻和后瞻断言:

    import  re
    
    s = "http://www.google.com 127.0.0.1"
    
    print(re.sub("(?<=[a-z])\.(?=[a-z])", "(dot)", s))
    http://www(dot)google(dot)com 127.0.0.1
    

    要处理字母和数字,这应该可以解决问题,确保至少有一个字母:

    s = "http://www.googl1.2com 127.0.0.1"
    
    print(re.sub("(?=.*[a-z])(?<=\w)\.(?=\w)", "(dot)", s, re.I))
    
    http://www(dot)googl1(dot)2com 127.0.0.1
    

    对于您的文件,您需要re.M

    In [1]: cat test.txt
    google8.com
    google9.com
    192.60.10.10
    33.44.55.66
    google10.com
    192.168.1.1
    google11.com
    
    In [2]: with open("test.txt") as f:
       ...:         import re
       ...:         print(re.sub("(?=.*[a-z])(?<=\w)\.(?=\w)", "(dot)", f.read(), re.I|re.M))
       ...:     
    google8(dot)com
    google9(dot)com
    192.60.10.10
    33.44.55.66
    google10(dot)com
    192.168.1.1
    google11(dot)com
    

    如果文件很大并且内存是一个问题,您也可以逐行执行,将所有行存储在列表中或随时使用每一行:

    import re
    with open("test.txt") as f:
        r = re.compile("(?=.*[a-z])(?<=\w)\.(?=\w)", re.I)
        lines = [r.sub("(?=.*[a-z])(?<=\w)\.(?=\w)", "(dot)") for line in f]
    

    【讨论】:

    • 好。这几乎适用于所有情况。但是,数字在域名中是合法的。此外,这取决于以小写形式存储的域名。
    • @StevenRumbalski,我只是基于 OP 自己的代码,事实上他们只在他们的模式中使用 [a-z],我把标志留给了 OP
    • 很公平。对于它的价值,我确实赞成你的回答。但我仍然认为它无法处理'io9.com' 是一个大问题。案例的问题很小,很容易解决。
    • @StevenRumbalski,别担心,我正在为涉及数字的情况拼凑一些东西
    • 我对此进行了测试,它适用于 1 或 2 行文本,但它不起作用。我的意思是它只会替换文本的开头,对于具有内部的文本文件:google9.comgoogle9.com192.60.10.10 33.44.55.66 它只会替换第一个 url
    【解决方案2】:

    从您的代码来看,您希望替换您的模式中的第一组。但是,re.sub 替换了整个匹配模式,而不是一个组。在您的情况下,这是句点之前的单个字符,句点本身和之后的单个字符。

    即使 sub 像您希望的那样工作,您的正则表达式也会丢失数字是 URL 一部分的边缘情况,例如 www.2048game.com。 定义 IP 的外观要容易得多。它总是一组四个数字,每个数字有一个、两个或三个数字,用点分隔。 (无论如何,在 IPv4 的情况下。但 IPv6 不使用句点,所以这里没关系。)

    假设您的文本文件中只有 URL 和 IP,只需过滤掉所有 IP,然后替换剩余 URL 中的句点:

    is_ip = re.compile('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}')
    urls = content.split(" ")
    for i, url in enumerate(urls):
        if not is_ip.match(url):
            urls[i] = url.replace('.', '(dot)')
    content = ' '.join(urls)
    

    当然,如果您在 content 中有常规文本,这也将替换所有常规句点,而不仅仅是 URL。在这种情况下,您首先需要更复杂的 URL 检测。见In search of the perfect URL validation regex

    【讨论】:

    • 测试了这个,它也替换了ip地址中的点
    • 抱歉,代码中有错字。你当然应该打电话给is_ip.match(url),而不是is_ip.match(content)
    • 很好,但为什么要改变输出结构呢?我的意思是,当我打开包含保存内容的文本文档时,它全部排成一行,而不是与更改之前的结构相同。
    • @Vlad 啊,我的另一张纸条。 split() 不使用任何空格分割参数,无论是空格字符还是换行符。请改用split(' ')
    【解决方案3】:
    import re
    
    content = "I tried to do this using regex, but my output is http://www.googl.com 192.60.10.10 33.44.55.66\nhttp://ya.ru\n..."
    
    reg = r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+'
    
    all_urls = re.findall(reg, content, re.M| re.I| re.DOTALL)
    repl_urls = [u.replace('.', '(dot)') for u in all_urls]
    
    for u, r in zip(all_urls, repl_urls):
        content = content.replace(u, r)
    
    print content
    

    【讨论】:

      【解决方案4】:

      您必须在点之前和之后存储[a-z] 内容,以便将其再次放入替换的字符串中。这是我如何解决的:

      from __future__ import print_function
      import sys
      import re
      
      nargs = len(sys.argv)
      if nargs < 2:
          sys.exit('You did not specify a file')
      else:
          inputFile = sys.argv[1]
          fp = open(inputFile)
          content = fp.read()
      
      replace = '\\1(dot)\\3'
      regex = '(.*[a-z])(\.)([a-z].*)'
      print(re.sub(regex, replace, content, re.M| re.I| re.DOTALL))
      

      【讨论】:

      • 'www.google.com' 提供'www.google(dot)com' 时失败。此外,数字在域名中是合法的,所以这也会失败(考虑 io9.com)。
      • 问题是匹配不重叠。请参阅 Padraic Cunningham 的回答,了解通过前瞻和后瞻断言解决此问题的方法。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-04-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多