【问题标题】:how to substite last occurrance of pattern in large file efficently如何有效地替换大文件中最后一次出现的模式
【发布时间】:2012-01-25 12:02:11
【问题描述】:

给定一个包含以下内容的文件:

<root>
<a></a>
<b></b>
</root>

命令应该输出:

<root>
<a></a>
<b></b>

我使用sedGNU Win32 端口尝试过的事情:

删除最后两行。

这很快,但它假定&lt;/root&gt; 是倒数第二行,否则会导致错误。

sed -e '$d' test.xml | sed -e '$d'

用空字符串替换所有出现的&lt;/root&gt;

这可行,但比第一个解决方案慢,并且如果有嵌套的 &lt;root&gt; 元素会中断(不太可能)。

sed -e 's|</root>||' test.xml

我正在处理的文件可能很大,因此效率很重要。

有没有办法将 sed 替换限制在文件中的最后一次出现?还是有其他更快的实用程序?

【问题讨论】:

    标签: windows bash text command-line sed


    【解决方案1】:

    将 Perl 与 File::Backwards 一起使用应该非常快(相对,我知道,但仍然......)。 Perlfaq5 有a topic 用于向后浏览文件并删除行。您可以使用本主题的代码作为起点来检查您的模式。

    【讨论】:

      【解决方案2】:

      sed:

      sed -e ':a;N;$!ba;s|\(.*\)</root>\n\(.*\)|\1\2|'
      

      【讨论】:

        【解决方案3】:

        为此使用awk 怎么样。

        AWK:

        awk '/^<\/root>$/{next}/<\/root>/{sub(/<\/root>/,"");print;next}1' filename
        

        第一个 /pattern/{action} 语句查找带有only &lt;/root&gt; 的行。它模式找到它,行动忽略它。

        第二个 /pattern/{action} 语句在行中查找包含&lt;/root&gt; anywhere 的行。如果 pattern 找到它,sub function 将其替换为空并打印该行的其余部分。

        第三个动作 1 对于其中没有模式 &lt;/root&gt; 的所有行都是正确的。如果它找到它,它会打印它。

        我做了一个快速测试,结果是这样的 -

        测试:

        [jaypal:~/Temp] cat tmp
        <root>
        <a></a>
        <b></b>
        </root>
        <root>
        <a></a>
        <b></b>
        </root><root>
        <a></a>
        <b></b></root>
        [jaypal:~/Temp] awk '/^<\/root>$/{next}/<\/root>/{sub(/<\/root>/,"");print;next}1' tmp
        <root>
        <a></a>
        <b></b>
        <root>
        <a></a>
        <b></b>
        <root>
        <a></a>
        <b></b>
        

        SED:

        这也应该有效。虽然它会删除所有 &lt;/root&gt; 而不仅仅是最后一次出现。

        sed '/<\/root>/,$s///' filename
        

        【讨论】:

          【解决方案4】:

          这可能对你有用:

           sed '/<\/root>/,/<root>/{/<\/root>/{h;d};H;//{x;p};${x;s/[^\n]*\n//p};d}' file
          

          这假定每个 &lt;root&gt; 标记都与一个结束 &lt;/root&gt; 标记匹配,并且这些标记出现在不同的行上(根据示例)。

          解释:

          1. 关注结束 &lt;/root&gt; 标记和开始 &lt;root&gt; 标记或文件结尾之间的行。
          2. 如果是关闭&lt;/root&gt;标签,将其保存在保持空间(HS)中,然后将其删除并开始新的循环。
          3. 对于焦点内的所有其他行(参见第 1 点),请将它们附加到 HS。
          4. 如果是并且打开&lt;root&gt;标签,切换到HS并打印出它的内容。
          5. 如果是文件结尾,即&lt;/root&gt; 标记和文件最后一行之间,交换到HS,删除第一行,即结束&lt;/root&gt; 标记并打印剩余部分。
          6. 对于焦点内的所有行,删除并开始一个新的循环。

          两遍的替代解决方案:

          sed -n '/<\/root>/=' file | sed -n '$s/$/d/p' | sed -f - file
          

          解释:

          1. 打印出关闭&lt;/root&gt;标签的行号
          2. 从最后匹配的行号生成一个 sed 删除命令。
          3. 将命令通过管道传递给读取源文件的 sed 实例。

          【讨论】:

            【解决方案5】:

            使用时间函数来查看哪个是有效的。 sed 应该是有效的。

            $time command
            

            在我看来,没有什么比 grep 更快的了。用 awk index() 试试看是否更快。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-07-21
              • 1970-01-01
              • 2015-09-01
              • 2019-12-11
              • 2012-04-23
              • 2015-03-28
              • 2022-11-22
              • 2011-02-03
              相关资源
              最近更新 更多