使用正则表达式替换文件数据

Posted

技术标签:

【中文标题】使用正则表达式替换文件数据【英文标题】:Using regex to replace file data 【发布时间】:2017-06-17 10:05:05 【问题描述】:

在here 的帮助下,我几乎可以完全按照我想要的方式工作。现在我需要能够添加在比较文件之前从文件中删除数据的功能。

原因是我要删除的字符串“数据”在每次保存文件时都会有所不同。

我已经编写了一个正则表达式来选择我想要删除的确切文本,但是我无法用我当前的代码来实现它。

以下是三个主要功能

HOSTNAME_RE = re.compile(r'hostname +(\S+)')
def get_file_info_from_lines(filename, file_lines):
    hostname = None
    a_hash = hashlib.sha1()
    for line in file_lines:
        a_hash.update(line.encode('utf-8'))
        match = HOSTNAME_RE.match(line)
        if match:
            hostname = match.group(1)
    return hostname, filename, a_hash.hexdigest()

def get_file_info(filename):
    if filename.endswith(('.cfg', '.startup', '.confg')):
        with open(filename, "r+") as in_file:
            #filename = re.sub(REMOVE_RE, subst, filename, 0, re.MULTILINE)
            return get_file_info_from_lines(filename, in_file.readlines())

def hostname_parse(directory):
    results = 
    i = 0
    l = len(os.listdir(directory))
    for filename in os.listdir(directory):
        filename = os.path.join(directory, filename)
        sleep(0.001)
        i += 1
        progress_bar(i, l, prefix = 'Progress:', suffix = 'Complete', barLength = 50)
        info = get_file_info(filename)
        if info is not None:
            results[info[0]] = info
    return results

这是查找要删除的字符串的正则表达式。

REMOVE_RE = r"((?:\bCurrent configuration)(?:.*\n?)6)"
subst = ""

EXAMPLE_FILE_BEFORE_DATA_REMOVED:

Building configuration...

Current configuration : 45617 bytes
!
! Last configuration change at 00:22:36 UTC Sun Jan 22 2017 by user
! NVRAM config last updated at 00:22:43 UTC Sun Jan 22 2017 by user
!
version 15.0
no service pad
!
no logging console
enable secret 5 ***encrypted password***
!
username admin privilege 15 password 7 ***encrypted password***
username sadmin privilege 15 secret 5 ***encrypted password***
aaa new-model
!
ip ftp username ***encrypted password***
ip ftp password 7 ***encrypted password***
ip ssh version 2
!
line con 0
 password 7 ***encrypted password***
 login authentication maint
line vty 0 4
 password 7 ***encrypted password***
 length 0
 transport input ssh
line vty 5 15
 password 7 ***encrypted password***
 transport input ssh
!

EXAMPLE_FILE_AFTER_DATA_REMOVED:

Building configuration...

!
no service pad
!
no logging console
enable 
!
username admin privilege 15 
username gisadmin privilege 15 
aaa new-model
!
ip ftp username cfgftp
ip ftp 
ip ssh version 2
!
line con 0

 login authentication maint
line vty 0 4

 length 0
 transport input ssh
line vty 5 15

 transport input ssh
!

我尝试在 get_file_info 和 get_file_info_from_lines 中执行类似 #filename = re.sub(REMOVE_RE, subst, filename, 0, re.MULTILINE) 的操作,但显然我没有正确实现。

如果我刚刚学习,任何帮助将不胜感激。

运行比较:

results1 = hostname_parse('test1.txt')
results2 = hostname_parse('test2.txt')



for hostname, filename, filehash in results1.values():
    if hostname in results2:
        _, filename2, filehash2 = results2[hostname]
        if filehash != filehash2:
            print("%s has a change (%s, %s)" % (
                hostname, filehash, filehash2))
            print(filename)
            print(filename2)
            print()

我不想修改当前文件。如果所有这些都可以在内存中完成,或者一个临时文件会很棒。

完整代码:

import hashlib
import os
import re


HOSTNAME_RE = re.compile(r'hostname +(\S+)')
REMOVE_RE = re.compile(r"((?:\bCurrent configuration)(?:.*\n?)6)")


def get_file_info_from_lines(filename, file_lines):
    hostname = None
    a_hash = hashlib.sha1()
    for line in file_lines:
        #match = HOSTNAME_RE.match(line)
        if not re.match(REMOVE_RE, line):
            a_hash.update(line.encode('utf-8'))
        #=======================================================================
        # if match:
        #     hostname = match.group(1)
        #=======================================================================
    return hostname, filename, a_hash.hexdigest()

def get_file_info(filename):
    if filename.endswith(('.cfg', '.startup', '.confg')):
        with open(filename, "r+") as in_file:
            return get_file_info_from_lines(filename, in_file.readlines())

def hostname_parse(directory):
    results = 
    for filename in os.listdir(directory):
        filename = os.path.join(directory, filename)
        info = get_file_info(filename)
        if info is not None:
            results[info[0]] = info
    return results


results1 = hostname_parse('test1') #Directory of test files
results2 = hostname_parse('test2') #Directory of test files 2



for hostname, filename, filehash in results1.values():
    if hostname in results2:
        _, filename2, filehash2 = results2[hostname]
        if filehash != filehash2:
            print("%s has a change (%s, %s)" % (
                hostname, filehash, filehash2))
            print(filename)
            print(filename2)
            print()

【问题讨论】:

有什么帮助吗?我仍然无法让它正常工作。 能否请您添加完整代码 - 以便我们知道这里使用了哪些库?无法让您的代码正常工作!对不起 @Md.SifatulIslam 完成。 你能验证两个文件之间的差异只有那一行吗? “差异文件 1 文件 2” @velotron 是的,文件是相同的副本,除了我为验证文本块实际上被忽略而修改的部分。 【参考方案1】:

我能够找到绕过正则表达式的方法。我只是通过匹配行来删除行。

def get_file_info_from_lines(filename, file_lines):
    hostname = None
    a_hash = hashlib.sha1()
    for line in file_lines:
        if "! Last " in line:
            line = ''
        if "! NVRAM " in line:
            line = ''
        a_hash.update(line.encode('utf-8'))
        match = HOSTNAME_RE.match(line)
        if match:
            hostname = match.group(1)

【讨论】:

【参考方案2】:

您好,我建议您使用以下方法: 使用一个函数来清理一条线。删除空的流程线。

然后使用Difflib进行比较。 使用python -m doctest file.py检查doctest

import re
source_content = """
Building configuration...

Current configuration : 45617 bytes
!
! Last configuration change at 00:22:36 UTC Sun Jan 22 2017 by user
! NVRAM config last updated at 00:22:43 UTC Sun Jan 22 2017 by user
!
version 15.0
no service pad
!
no logging console
enable secret 5 ***encrypted password***
!
username admin privilege 15 password 7 ***encrypted password***
username sadmin privilege 15 secret 5 ***encrypted password***
aaa new-model
!
ip ftp username ***encrypted password***
ip ftp password 7 ***encrypted password***
ip ssh version 2
!
line con 0
 password 7 ***encrypted password***
 login authentication maint
line vty 0 4
 password 7 ***encrypted password***
 length 0
 transport input ssh
line vty 5 15
 password 7 ***encrypted password***
 transport input ssh
!
"""

target_content = """
Building configuration...

!
no service pad
!
no logging console
enable 
!
username admin privilege 15 
username gisadmin privilege 15 
aaa new-model
!
ip ftp username cfgftp
ip ftp 
ip ssh version 2
!
line con 0

 login authentication maint
line vty 0 4

 length 0
 transport input ssh
line vty 5 15

 transport input ssh
!
"""



HOSTNAME_RE = re.compile(r'hostname +(\S+)')
REMOVE_RE = re.compile(r"((?:\bCurrent configuration)(?:.*\n?)6)")


def process_line(line):
    """
    >>> process_line('! rgrg')
    '!'
    >>> process_line('username admin privilege 15 password 7 ***encrypted password***')

    """

    if line.startswith('!'):
        return '!'
    if HOSTNAME_RE.match(line):
        return match.group(1)
    if REMOVE_RE.match(line):
        return ''
    return line

#debug
for line in source_content.split('\n'):
    print(repr(process_line(line).strip()))

whitened = '\n'.join(process_line(line).strip() 
                     for line in source_content.split('\n'))

def clean_lines(lines, flag=''):
    """ Replaces multiple 'flag' lines by only one. 
    """
    res = []
    in_block = False
    for line in lines:

        if line.strip('\n') == flag:
            if not in_block:
                res.append(line)
                in_block = True
            continue
        in_block = False
        res.append(line)
    return res

print('^^^^^^^^^^^^^^')
no_exc = '\n'.join(clean_lines(whitened.split('\n'), flag='!'))
print(no_exc)
print('##############')
no_sp = '\n'.join(clean_lines(no_exc.split('\n')))        
print(no_sp)  

【讨论】:

这是我见过的最接近实际删除我不想包含的行的方法。看来我可能不得不将整个方法重新回到绘图板上。 没问题。你在架构上犯了初学者的错误。它发生在每个人身上。看看这个视频和示例代码:youtube.com/watch?v=DJtef410XaM我刚刚应用了他的建议:)。【参考方案3】:

get_file_info_from_lines 中,如果它与您的正则表达式匹配,则只需忽略该行。这样您就不需要实际修改文件或创建另一个文件,您只需使用实际重要的行计算哈希即可。

for line in file_lines:
    if not re.match(REMOVE_RE, line):
        a_hash.update(line.encode('utf-8'))

【讨论】:

这看起来应该可以工作,但它仍然说有区别。除了显示当前配置的部分外,这两个文件是相同的。我只是添加或删除几个字符进行测试。 我刚刚运行了一个快速测试,以防我遗漏了一些东西,但它工作正常。您的代码肯定有其他问题。 好吧,我的代码与上面的代码相同,只是您所做的更改,它仍然返回文件不同。您是否更改了当前配置中的行?就像将“按用户”更改为其他内容? 2017 年 1 月 22 日星期日,例如 user1234 在一个文件中而不是另一个文件中? 然后在文件末尾添加附加文本是另一个测试。

以上是关于使用正则表达式替换文件数据的主要内容,如果未能解决你的问题,请参考以下文章

用正则表达式替换某一区段内的字符,在线等

使用 Vim 的正则表达式替换多个文件的单词在 sed 中无法按预期工作

在多个文件中查找和替换正则表达式的最佳工具是啥?

使用正则表达式替换多个标题名称

如何使用正则表达式但以另一种格式替换文件的相同名称?

在熊猫数据框中使用正则表达式替换列值