如何有效地替代大文件中的模式发生

给定具有以下内容的文件:

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

该命令应输出:

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

我尝试过使用sedGNU Win32端口:

删除最后两行。

这很快,但是假设</root>是倒数第二行,如果不是,则会导致一个错误。

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

将所有</root>replace为空string。

这可以工作,但比第一个解决scheme慢,如果有嵌套的<root>元素(不太可能),将会中断。

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

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

有没有办法限制sed替代文件中的最后一个事件? 或者还有其他一些效用会更快吗?

使用Perl和File :: Backwards应该是非常快的(相对的,我知道,但仍…)。 Perlfaq5有一个向后翻阅文件和删除行的主题。 您可以使用此主题的代码作为起点来检查您的模式。

sed

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

如何使用awk这个。

AWK:

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

First /pattern/{action}语句只用 </root>查找行。 它发现它,行动忽略它。

Second /pattern/{action}语句在行中的任意位置查找包含</root>的行。 如果模式发现它, sub function替换它没有和打印行的其余部分。

对于所有没有模式</root>的行,第一个是1 。 如果找到了,就打印出来。

我做了一个快速测试,这是结果 –

测试:

 [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:

这也应该工作。 虽然它会删除所有</root>而不是最后一个事件。

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

这可能适合你:

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

这假定每个<root>标签都与一个关闭的</root>标签相匹配,并且这些标签出现在不同的行上(如示例所示)。

说明:

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

有两个通行证的替代解决方案:

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

说明:

  1. 打印关闭</root>标签的行号
  2. 从最后一个匹配的行号生成一个sed delete命令。
  3. 将命令传递给读取源文件的sed实例。

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

 $time command 

在我看来,没有比grep更快的东西了。 尝试用awk index()来查看它是否更快。