【问题标题】:Remove multiple lines in Python在 Python 中删除多行
【发布时间】:2016-01-01 20:33:19
【问题描述】:

我有一个如下所示的文件:

<VirtualHost *:80>
    ServerName Url1
    DocumentRoot Url1Dir
</VirtualHost>

<VirtualHost *:80>
    ServerName Url2
    DocumentRoot Url2Dir
</VirtualHost>

<VirtualHost *:80>
    ServerName REMOVE
</VirtualHost>

<VirtualHost *:80>
    ServerName Url3
    DocumentRoot Url3Dir
</VirtualHost>

我想删除这段代码的地方(它不会改变):

<VirtualHost *:80>
    ServerName REMOVE
</VirtualHost>

我尝试使用下面的代码找到整段代码,但它似乎不起作用。

with open("out.txt", "wt") as fout:
        with open("in.txt", "rt") as fin:
            for line in fin:
                fout.write(line.replace("<VirtualHost *:80>\n    ServerName REMOVE\n</VirtualHost>\n", ""))

【问题讨论】:

  • 你不应该做fin.read() 什么的吗? for line in fin 会这样工作吗?如果是这样,您正在逐行读取文件,因此替换 3 行将不起作用...
  • 这看起来像一个 XML 文件,所以也许您可以使用 XML 解析器来完成该任务。例如。 lxmlbeautifulsoup.
  • 正如 karlson 所写,您可能应该更新问题以表达目标。更好的解决方案可能与您现在尝试的略有不同。您甚至可以使用内置模块 xml.etree.ElementTree
  • @pepr 是的,但是我现在拥有的代码也将与我将在未来制作的不是 XML 的东西一起使用,因此它与删除无关XML。

标签: python


【解决方案1】:

最快的方法是将整个文件读入一个字符串,执行替换,然后将字符串写入您需要的文件。例如:

#!/usr/bin/python

with open('in.txt', 'r') as f:
      text = f.read()

      text = text.replace("<VirtualHost *:80>\n    ServerName REMOVE\n</VirtualHost>\n\n", '')

      with open('out.txt', 'w') as f:
            f.write(text)

【讨论】:

    【解决方案2】:

    这里是有限自动机解决方案,可以在以后的开发过程中轻松修改。一开始可能看起来很复杂,但请注意,您可以单独查看每个状态值的代码。您可以在纸上绘制图表(节点为圆形,箭头为定向边)以了解所做的工作概览

    status = 0      # init -- waiting for the VirtualHost section
    lst = []        # lines of the VirtualHost section
    with open("in.txt") as fin, open("out.txt", "w") as fout:
        for line in fin:
    
            #-----------------------------------------------------------
            # Waiting for the VirtualHost section, copying.
            if status == 0: 
                if line.startswith("<VirtualHost"):
                    # The section was found. Postpone the output.
                    lst = [ line ]  # first line of the section
                    status = 1
                else:
                    # Copy the line to the output.
                    fout.write(line)
    
            #-----------------------------------------------------------
            # Waiting for the end of the section, collecting.
            elif status == 1:   
                if line.startswith("</VirtualHost"):
                    # The end of the section found, and the section
                    # should not be ignored. Write it to the output.
                    lst.append(line)            # collect the line
                    fout.write(''.join(lst))    # write the section
                    status = 0  # change the status to "outside the section"
                    lst = []    # not neccessary but less error prone for future modifications
                else:
                    lst.append(line)    # collect the line
                    if 'ServerName REMOVE' in line: # Should this section to be ignored?
                        status = 2      # special status for ignoring this section
                        lst = []        # not neccessary 
    
            #-----------------------------------------------------------
            # Waiting for the end of the section that should be ignored.
            elif status == 2:   
                if line.startswith("</VirtualHost"):
                    # The end of the section found, but the section should be ignored.
                    status = 0  # outside the section
                    lst = []    # not neccessary
    

    【讨论】:

    • 我实际上正在使用这个脚本,因为它还可以删除 DocumentRoot 行,只需进行一些编辑。谢谢!
    • :) 添加更多elif status == x: 时,最好在未实施新状态的情况下也添加else: - 并进行一些诊断。我的经验是最好不要重新编号状态值,只需添加一个新值。有限自动机可能需要一些特殊情况,例如文件意外结束等。然后我选择像555这样的可见状态数字。
    【解决方案3】:

    虽然上述答案是一种务实的方法,但它首先是脆弱且不灵活的。
    这里有一些不那么脆弱的东西:

    import re
    
    def remove_entry(servername, filename):
        """Parse file , look for entry pattern and return new content
    
        :param str servername: The server name to look for
        :param str filename: The file path to parse content
        :return: The new file content excluding removed entry
        :rtype: str
        """
        with open(filename) as f:       
            lines = f.readlines()        
            starttag_line = None
            PATTERN_FOUND = False       
    
            for line, content in enumerate(lines):
                if '<VirtualHost ' in content: 
                    starttag_line = line       
                # look for entry
                if re.search(r'ServerName\s+' + servername, content, re.I):
                    PATTERN_FOUND = True
                # next vhost end tag and remove vhost entry
                if PATTERN_FOUND and '</VirtualHost>' in content:
                    del lines[starttag_line:line + 1]
                    return "".join(lines)        
    
    
    filename = '/tmp/file.conf'
    
    # new file content
    print remove_entry('remove', filename)
    

    【讨论】:

      猜你喜欢
      • 2016-06-14
      • 1970-01-01
      • 1970-01-01
      • 2021-04-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多