2012-08-22 36 views
1

我使用提取XML标记之间的内容如下: -出的内存处理大型文件用Perl,桑达,AWK

perl -lne 'BEGIN{undef $/} while (/<tagname>(.*?)<\/tagname>/sg){print $1}' input.txt > output.txt 

不幸的是我得到out of memory的问题,我知道我可以分裂文件和进程每个然后concat,但我想知道是否有另一种方式,无论是对上述修改或使用喜欢的awk或sed?

input.txt文件大小17GB和70GB之间变化。

编辑:

输入文件可以是任何的XML文件,一个点要注意的是,它不包含任何换行符,例如: -

<body><a></a><b></b><c></c></body><foo></foo><bar><z></z></bar>

+1

请给输入文件的摘录 –

+0

输入文件可以是任何XML文件。我应该做的一点是它没有换行符。 –

回答

3

该一衬垫整个文件读入存储器中作为一个巨大的“线”。当然,你会遇到内存问题,内存容量为17GB或更多!逐行读取并处理文件,或者使用read来取代合适大小的块。

在这种情况下,搜索<tagname>,注意其线位置,搜索结束标记从那里开始。如果您没有找到它,请将当前行/块填充到缓冲区中并重复,直到您在文件中的其他行上找到它为止。找到时,打印出此缓冲区并将其清空。重复,直到文件结束。

请注意,如果你会使用任意大小的块,你必须考虑到通过边界由块尾切割不完整标签和填充它“处理”缓冲拆分标签的可能性。

+0

输入文件不包含换行符。 :( –

+0

@martinblank,使用'read'。更新回答 –

+0

perfecto谢谢 –

0

我会申请一个过滤器,输入文件介绍换行符。也许在每个</tagname>之后?然后,您将能够通过您的perl命令 摆脱BEGIN{undef $/},并通过处理“合理”记录来避免内存问题。

3

解析大文件有可能需要像XML::LibXML::Reader拉解析器。这里有一个例子:

#!/usr/bin/perl 
use warnings; 
use strict; 

use XML::LibXML::Reader; 

my $reader = XML::LibXML::Reader->new(location => 'input.txt') or die; 

while ($reader->read) { 
    if ($reader->nodePath =~ m{/tagname$}     # We are at <tagname> or </tagname>. 
     and $reader->nodeType == XML_READER_TYPE_ELEMENT) { # Only the start tag is interesting. 
     print $reader->readInnerXml; 
    } 
} 
+0

+1对XML数据使用XML解析器。正则表达式是错误的工具。 –

3

为了从文件中读取较小的块,你可以设置你的输入记录分隔符的结束标记:

BEGIN { $/ = "</tagname>"; } 

下面是一个例子:

代码:

perl -lnwe 'BEGIN { $/ = "</tagname>"; } print;' 

输入:

<tagname>foo</tagname><tagname>bar</tagname><tagname>baz</tagname><tagname>baf</tagname> 

输出:

<tagname>foo 
<tagname>bar 
<tagname>baz 
<tagname>baf 

你会注意到,结束标记丢失,那就是因为你还可以使用-l选项包括chomp,从而消除输入记录分隔符。如果您不想要这种行为,只需删除-l选项并在打印语句中插入换行符。

注:

我会说这是一个黑客颇有几分,但它确实符合你已经在使用,即敏感匹配的情况下,精确的标签。

你可以做什么来补偿是使用您正则表达式的这里面:

perl -lnwe 'BEGIN { $/ = "</tagname>"; } 
    while (/<tagname>(.*?)<\/tagname>/sg) { print $1 }' input.txt > output.txt 

,或者可能使用XML解析器解析块。

如果别人建议的XML解析器不会为这种巨大的文件工作,这可能是读取数据的更小的块,而不在半冒着切割标签的方式。

0

目前尚不清楚输入文件是否你是格式良好的XML与否。你给的例子不是XML(没有根元素)。如果数据为XML,则可以使用XML::Twig附带的工具xml_grepxml_grep -r tagname --text_only mybig.xml这将适用于任何大小的文件,前提是每个匹配的元素都可以放在内存中。

如果这个速度太慢,你也许可以通过直接XML解析器::获得一些速度,代码将不会很复杂写。它更容易不有,虽然它写; - )

1

您还可以使用awk来打破一个大,一个行文件。 Sed会在内存不足时尝试加载完整行,但在awk中(如perl),您可以定义要作为“换行符”使用的内容,绕过问题。

对Perl,你已经有了上面的一个例子,这里是一个AWK:

cat big-one-line-file | awk 'BEGIN { RS=">" } ; {print $0">"}' 

请注意,在文件的结尾,一个额外>将显示,如果该文件不是结束一个“>”。您可以通过任何方式删除它(如后清理sed:sed '$ s/>$//')或调整脚本。

正如我也有这个问题,并帮助别人,我会添加更多的例子来帮助测试。

您可以用dd提取文件的一小部分,赶上做大“记录分隔符”,像工程或标签测试脚本。例如:

dd if=big-one-line-file.xml bs=8192 count=10 | awk ' BEGIN { RS="<tag 123>" } ; NR>1 {print "<tag 123>"$0} ; NR==1 {print $0} ' 

提取物中的大的一线路file.xml的第一80KB和打破在“”的文件。为避免在文件开始时出现额外的(和错误的)“”,请以不同的方式处理它(即:不要触摸它)

使用dd选项skip={# of blocks to reach near the file size}来提取文件的结尾而不是顶部因为它总是只有一行)。我使用了skip = 100000000,并开始删除零直到出现并调整了块号。