Как я могу использовать регулярные выражения Perl для разбора данных XML? - PullRequest
0 голосов
/ 01 июня 2010

У меня есть довольно длинный кусок XML, который я хочу проанализировать. Я хочу удалить все, кроме кода подкласса и города. Так что у меня осталось что-то вроде примера ниже.

Пример

ИСПЫТАТЕЛЬНЫЙ ПОДКЛАСС | МАЙАМИ

код

<?xml version="1.0" standalone="no"?>  
<web-export>  
<run-date>06/01/2010  
<pub-code>TEST  
<ad-type>TEST  
<cat-code>Real Estate</cat-code>  
<class-code>TEST</class-code>  
<subclass-code>TEST SUBCLASS</subclass-code>  
<placement-description></placement-description>  
<position-description>Town House</position-description>  
<subclass3-code></subclass3-code>  
<subclass4-code></subclass4-code>  
<ad-number>0000284708-01</ad-number>  
<start-date>05/28/2010</start-date>  
<end-date>06/09/2010</end-date>  
<line-count>6</line-count>  
<run-count>13</run-count>  
<customer-type>Private Party</customer-type>  
<account-number>100099237</account-number>  
<account-name>DOE, JOHN</account-name>  
<addr-1>207 CLARENCE STREET</addr-1>  
<addr-2> </addr-2>  
<city>MIAMI</city>  
<state>FL</state>  
<postal-code>02910</postal-code>  
<country>USA</country>  
<phone-number>4014612880</phone-number>  
<fax-number></fax-number>  
<url-addr> </url-addr>  
<email-addr>noemail@ttest.com</email-addr>  
<pay-flag>N</pay-flag>  
<ad-description>DEANESTATES2BEDS2BATHSAPPLIANCED</ad-description>  
<order-source>Import</order-source>  
<order-status>Live</order-status>  
<payor-acct>100099237</payor-acct>  
<agency-flag>N</agency-flag>  
<rate-note></rate-note>  
<ad-content> MIAMI&#47;Dean Estates&#58; 2 
beds&#44; 2 baths&#46; Applianced&#46; Central air&#46; Carpets&#46; Laundry&#46; 2 decks&#46; Pool&#46; Parking&#46; Close to everything&#46;No smoking&#46; No utilities&#46; &#36;1275 mo&#46; 401&#45;578&#45;1501&#46;  </ad-content>  
</ad-type>  
</pub-code>  
</run-date>  
</web-export>  

PERL

Итак, я хочу открыть существующий файл, прочитать его содержимое и использовать регулярные выражения для удаления ненужных тегов XML.

open(READFILE, "FILENAME");  
while(<READFILE>)  
{  
$_ =~ s/<\?xml version="(.*)" standalone="(.*)"\?>\n.*//g;  
    $_ =~ s/<subclass-code>//g;  
    $_ =~ s/<\/subclass-code>\n.*/|/g;  
    $_ =~ s/(.*)PJ RER Houses /PJ RER Houses/g;  
    $_ =~ s/\G //g;  
    $_ =~ s/<city>//g; 
    $_ =~ s/<\/city>\n.*//g;  
    $_ =~ s/<(\/?)web-export>(.*)\n.*//g;  
    $_ =~ s/<(\/?)run-date>(.*)\n.*//g;  
    $_ =~ s/<(\/?)pub-code>(.*)\n.*//g;  
    $_ =~ s/<(\/?)ad-type>(.*)\n.*//g;  
    $_ =~ s/<(\/?)cat-code>(.*)<(\/?)cat-code>\n.*//g;  
    $_ =~ s/<(\/?)class-code>(.*)<(\/?)class-code>\n.*//g;  
    $_ =~ s/<(\/?)placement-description>(.*)<(\/?)placement-description>\n.*//g;  
    $_ =~ s/<(\/?)position-description>(.*)<(\/?)position-description>\n.*//g;  
    $_ =~ s/<(\/?)subclass3-code>(.*)<(\/?)subclass3-code>\n.*//g;  
    $_ =~ s/<(\/?)subclass4-code>(.*)<(\/?)subclass4-code>\n.*//g;  
    $_ =~ s/<(\/?)ad-number>(.*)<(\/?)ad-number>\n.*//g;  
    $_ =~ s/<(\/?)start-date>(.*)<(\/?)start-date>\n.*//g;  
    $_ =~ s/<(\/?)end-date>(.*)<(\/?)end-date>\n.*//g;  
    $_ =~ s/<(\/?)line-count>(.*)<(\/?)line-count>\n.*//g;  
    $_ =~ s/<(\/?)run-count>(.*)<(\/?)run-count>\n.*//g;  
    $_ =~ s/<(\/?)customer-type>(.*)<(\/?)customer-type>\n.*//g;  
    $_ =~ s/<(\/?)account-number>(.*)<(\/?)account-number>\n.*//g;  
    $_ =~ s/<(\/?)account-name>(.*)<(\/?)account-name>\n.*//g;  
    $_ =~ s/<(\/?)addr-1>(.*)<(\/?)addr-1>\n.*//g;  
    $_ =~ s/<(\/?)addr-2>(.*)<(\/?)addr-2>\n.*//g;  
    $_ =~ s/<(\/?)state>(.*)<(\/?)state>\n.*//g;  
    $_ =~ s/<(\/?)postal-code>(.*)<(\/?)postal-code>\n.*//g;  
    $_ =~ s/<(\/?)country>(.*)<(\/?)country>\n.*//g;  
    $_ =~ s/<(\/?)phone-number>(.*)<(\/?)phone-number>\n.*//g;  
    $_ =~ s/<(\/?)fax-number>(.*)<(\/?)fax-number>\n.*//g;  
    $_ =~ s/<(\/?)url-addr>(.*)<(\/?)url-addr>\n.*//g;  
    $_ =~ s/<(\/?)email-addr>(.*)<(\/?)email-addr>\n.*//g;  
    $_ =~ s/<(\/?)pay-flag>(.*)<(\/?)pay-flag>\n.*//g;  
    $_ =~ s/<(\/?)ad-description>(.*)<(\/?)ad-description>\n.*//g;  
    $_ =~ s/<(\/?)order-source>(.*)<(\/?)order-source>\n.*//g;  
    $_ =~ s/<(\/?)order-status>(.*)<(\/?)order-status>\n.*//g;  
    $_ =~ s/<(\/?)payor-acct>(.*)<(\/?)payor-acct>\n.*//g;  
    $_ =~ s/<(\/?)agency-flag>(.*)<(\/?)agency-flag>\n.*//g;  
    $_ =~ s/<(\/?)rate-note>(.*)<(\/?)rate-note>\n.*//g;  
    $_ =~ s/<ad-content>(.*)\n.*//g;  
    $_ =~ s/\t(.*)\n.*//g;  
    $_ =~ s/<\/ad-content>(.*)\n.*//g;  
}  
close( READFILE1 );  

Есть ли более простой способ сделать это? Я не хочу использовать какие-либо модули. Я знаю, что это может сделать это проще, но файл, который я читаю, содержит много данных.

Ответы [ 6 ]

12 голосов
/ 01 июня 2010

Это ужасно (извините).Регулярные выражения не обязательно быстрее, даже если у вас много данных.

Почему бы не использовать XSLT ?


Ваша таблица стилей в основном будет выглядеть так (если у вас есть только один элемент subclass-code и city):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output method="text" />  

    <xsl:template match="/">
        <xsl:apply-templates select="//subclass-code|//city" />
    </xsl:template>

    <xsl:template match="subclass-code">
       <xsl:value-of select="." /><xsl:text> | </xsl:text>
    </xsl:template>

    <xsl:template match="city">
       <xsl:value-of select="." /><xsl:text>  </xsl:text>
    </xsl:template>
</xsl:stylesheet>

(обновлен код для работыс несколькими элементами. Не может быть лучшим решением;))

7 голосов
/ 01 июня 2010

Почему бы вам не использовать библиотеки, если кто-то уже написал эффективный (и, смею сказать, многофункциональный) модуль типа XML :: Twig для анализа XML?

use XML::Twig;

die "Usage: give-me-the-elements.pl <xml_file>\n" unless ($ARGV[0]);

my $twig = XML::Twig->new( twig_handlers => 
                             { 'subclass-code' => sub { print->text, "|"; }, 
                               'city' => sub { print $_->text, "\n"; }, 
                             },
                           pretty_print  => 'indented');

$twig->parsefile($ARGV[0]); 
$twig->purge;
5 голосов
/ 01 июня 2010

Простой способ сделать это - использовать XML :: Simple в сочетании с дампером (мне нравится XXX , большинство используют Data :: Dumper . Это загрузит XML в структуру данных perl, где вы можете выбрать атрибуты, которые вы хотите (или не хотите, если вы предпочитаете просто явно delete).

Используя набор инструментов, который я только что предложил, вы можете увидеть работающий пример того, что вы хотите:

use strict;
use warnings;
use XML::Simple;

my $data = XML::Simple::parse_fh( \*DATA );       
my $sub = $data->{'run-date'}{'pub-code'}{'ad-type'};

foreach my $k ( keys %$sub ) {
  delete $sub->{$k}
    unless $k =~ /subclass-code|city/
  ; 
} 

use XXX;
XXX $data;
5 голосов
/ 01 июня 2010

Если вам нужен общий метод синтаксического анализа XML, не используйте регулярные выражения. Если вам просто нужно то, что вы сказали (удалите все, кроме кода подкласса и города) и если вы уверены, что эти два тега появятся без "странных" вещей внутри (xml-сущности, разделы CDATA) и что эти теги не будут появляться внутри других фрагментов CDATA и т. д., вы можете просто сделать:

$/ = undef; # slurp mode
open(READFILE, "FILENAME");
$t = <READFILE>;
close READFILE;
$t =~ s#^.*<subclass-code>(.*?)</subclass-code>.*<city>(.*?)</city>.*$#$1 - $2#s;
# in case the tags could appear in distinct order - uncomment the following
# $t =~ s#^.*<city>(.*?)</city>.*<subclass-code>(.*?)</subclass-code>.*$#$2 - $1#s;
print $t;

Редактировать: Немного более мощный (гм), следующие требования плаката:

while( $t =~ m#<pub-code>([^<\s]*).*?<subclass-code>(.*?)</subclass-code>.*?<city>(.*?)</city>#sg) {
  print "$1 : $2 | $3 \n";
}

Но, пожалуйста, остановитесь здесь и не идите дальше, этот путь ведет в ад ...

1 голос
/ 02 июня 2010

Обратите внимание на то, что говорили другие авторы, настоятельно рекомендуется избегать регулярных выражений при разборе языков разметки.

Тем не менее, чистый Perl способ выполнить то, что вы хотите без каких-либо модулей и предположить, что вышеупомянутые теги существуют:

my $reg_subclass = '\<city\>';
my $reg_city = '\<subclass\d*\-code\>';

open my $in, "input file";
open my $out, '>' ,"output file";
while ( my $line = <$in> ) {
    if ( $line =~ /$reg_subclass|$reg_city/ ) {
        print $out $line;
    }
}
close $in;
close $out;
0 голосов
/ 01 июня 2010

Я не эксперт в том, что поддерживает Perl, но в целом, я думаю, вы хотите использовать XPath здесь. (Это может быть то, что использует библиотека Twig выше, я не уверен).

Пример псевдо-Perl (прошу прощения за грубость; давно я действительно активно использовал Perl):

$subclassExpr = "/web-export/subclass-code/text()";
$cityExpr = "/web-export/city/text()";

$domObject = xml_dom_parse( $xml_doc );

$subClass = xpath_evaluate( $domObject, $subclassExpr );
$subClass = xpath_evaluate( $domObject, $cityExpr );
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...