2012-08-22 35 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,並開始刪除零直到出現並調整了塊號。