2014-09-18 47 views
0

我有一個大(> 2GB),XML,看起來大致是這樣的文件:Perl的正則表達式來刪除節點在XML

<record id="1"> 
    <a> 
     <detail>blah</detail> 
     .... 
     <detail>blah</detail> 
    </a> 
    <b> 
     <detail>blah</detail> 
     .... 
     <detail>blah</detail> 
    </b> 
    <c> 
     <detail>blah</detail> 
     .... 
     <detail>blah</detail> 
    </c> 
</record> 
... 
<record id="999999"> 
    <a> 
     <detail>blah</detail> 
     .... 
     <detail>blah</detail> 
    </a> 
    <b> 
     <detail>blah</detail> 
     .... 
     <detail>blah</detail> 
    </b> 
    <c> 
     <detail>blah</detail> 
     .... 
     <detail>blah</detail> 
    </c> 
</record> 

然而,我的實際文件沒有換行符爲每個節點(雖然也有少數線路中斷整個隨機分佈。)

我想有使用Perl來去除每個所有<b>節點節點,包括他們的後代。

所以 - 我得到的文件應該是這樣的:

<record id="1"> 
    <a> 
     <detail>blah</detail> 
     .... 
     <detail>blah</detail> 
    </a> 
    <c> 
     <detail>blah</detail> 
     .... 
     <detail>blah</detail> 
    </c> 
</record> 
... 
<record id="999999"> 
    <a> 
     <detail>blah</detail> 
     .... 
     <detail>blah</detail> 
    </a> 
    <c> 
     <detail>blah</detail> 
     .... 
     <detail>blah</detail> 
    </c> 
</record> 

這裏有一個重要的注意...正如我所說,該文件是大約2.4GB。對於較小的文件,我使用XMLReader和PHP成功解析文件並提取我需要的內容。但是,似乎PHP無法處理這麼大的文件(PHP < v.5.6使用32位文件指針)。因此,我的目標是使用像sedperl這樣的實用工具,通過剝去我不需要的大塊來減少文件大小。我知道「XML感知」的實用工具,將更適合這種類型的工作,但我還沒有找到一個可以處理這個大文件...

無論如何,我試過這個(使用@作爲我的分隔符):

perl -pe '[email protected]<b>.*</b>@@sg' input.xml > modified.xml 

但是,這並沒有奏效 - 它並沒有刪除任何節點。

我確定<b>節點沒有任何會破壞該模式的屬性。

很明顯 - 我是小白,當談到這一點,所以我敢肯定,我還差得遠呢......由

+3

我會建議使用一個實際的XML解析器像[XML :: Twig'](https://metacpan.org/pod/XML::Twig)。 – Miller 2014-09-18 20:48:37

+1

@。* @@ sg'應從第一個「」移至最後一個「」。它沒有刪除任何東西?可能是命令行Perl選項。通常情況下,你會在這種情況下使用一個懶惰的量詞,比如'@。*? @@ sg'即使這樣做,格式化也會被破壞。 – sln 2014-09-18 21:15:20

回答

6

XML::Twig可以用來切割從一個大的XML文件中的元素,而不必元件之間擔心空白:

use warnings; 
use strict; 
use XML::Twig; 

my $xml = do { local $/; <DATA> }; 

my $twig = XML::Twig->new(
    twig_handlers => { 
     'record/b' => sub { $_->cut() } 
    }, 
    pretty_print => 'indented' 
); 
$twig->parse($xml); 
$twig->print(); 

__DATA__ 
<?xml version="1.0" encoding="UTF-8"?> 
<top> 
    <record id="1"> 
     <a> 
      <detail>blah</detail> 
      <detail>blah</detail> 
     </a> 
     <b> 
      <detail>blah</detail> 
      <detail>blah</detail> 
     </b> 
     <c> 
      <detail>blah</detail> 
      <detail>blah</detail> 
     </c> 
    </record> 
    <record id="999999"> 
     <a> 
      <detail>blah</detail> 
      <detail>blah</detail> 
     </a> 
     <b> 
      <detail>blah</detail> 
      <detail>blah</detail> 
     </b> 
     <c> 
      <detail>blah</detail> 
      <detail>blah</detail> 
     </c> 
    </record> 
</top> 

這裏是輸出:

<?xml version="1.0" encoding="UTF-8"?> 
<top> 
    <record id="1"> 
    <a> 
     <detail>blah</detail> 
     <detail>blah</detail> 
    </a> 
    <c> 
     <detail>blah</detail> 
     <detail>blah</detail> 
    </c> 
    </record> 
    <record id="999999"> 
    <a> 
     <detail>blah</detail> 
     <detail>blah</detail> 
    </a> 
    <c> 
     <detail>blah</detail> 
     <detail>blah</detail> 
    </c> 
    </record> 
</top> 
+2

你也可以使用xml_grep,它附帶XML :: Twig,我認爲(對我來說測試它太遲了)'xml_grep -v'record/b'file.xml> new_file.xml'會起作用 – mirod 2014-09-18 21:23:18

+0

鑑於文件大小(〜2GB)是否適合談論[處理塊](https://metacpan.org/pod/XML::Twig#XML::Twig-101)? – Miller 2014-09-18 21:23:20

+2

@miller是的,你正在讀取內存中的整個文件,實際上只是在執行'XML :: Twig-> new(twig_roots => {'record/b'=> 1},twig_print_outside_roots => 1) - > parsefile( 「file.xml」)會輸出整個文件(因爲'print_outside_roots',而省略'b'元素)。 http://xmltwig.org/xmltwig/tutorial/yapc_twig_s4.html教程的第4.5節描述了這一點。 – mirod 2014-09-18 21:28:45

3

喲ucan使用這個正則表達式:

<b>[\s\S]+?<\/b> 

Working demo

enter image description here

的想法是消除<b>...</b>標籤

+0

爲什麼'[\ s \ S]'而不是'.'(用's'修飾符表示正則表達式)? – mirod 2014-09-18 21:30:20

+0

@mirod我認爲這也可以。好眼睛的人 – 2014-09-18 21:32:35

1

您可以使用XML::LibXML::Reader,一個libxml2的拉解析器:

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

use XML::LibXML::Reader; 

my $r = 'XML::LibXML::Reader'->new(location => 'file.xml'); 
while ($r->nextElement('record')) { 
    my $rec = $r->copyCurrentNode(1); 
    for my $del ($rec->findnodes('b')) { 
     $rec->removeChild($del); 
    } 
    print $rec; 
}