Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Perl: Общие вопросы > Разбор XML


Автор: AndreyCH 20.3.2009, 16:57
Вот для моего класса нужен метод, который распарсит строку и вычленить оттуда название тэга и его значение.
есть 2 метода.
1. файл util.pl
Код

#!/usr/bin/perl 
package UtilXml;
use strict;

sub UtilXml::GetTagData($$){
  my ($val, $tag) = @_;
  my @value = $val =~ m{<$tag>(.+?)</$tag>}gs;
  return @value;
}

sub UtilXml::SaveTagData($$){
  my ($val, $tag) = @_;
  return qq(<$tag>$val</$tag>);
}
1;


2. XML файл, который будем разбирать users.xml
Код

<user_list>
  <user>
    <id>3</id> 
    <name>Sergey</name>
    <lname>Petrov</lname>
  </user>
  <user>
    <id>4</id> 
    <name>Ivan</name>
    <lname>Ivanov</lname>
  </user>
</user_list>


Ну и код которым парсим test.pl
Код

#!/usr/bin/perl 
use strict;
require "util.pl";
open (INFILE, '<users.xml') or die 'cant open file';

undef $/; 

my $cFile = <INFILE>;
my @users;

print "-------------------------\n";

  my $tag = "user_list";
  my @vFile = UtilXml::GetTagData( $cFile, $tag);
  $tag = "user";
  my @users = UtilXml::GetTagData( $vFile[0], $tag);

  foreach my $value (@users){
#   $id=$name=$pass=$value;
    my $tag1 = "name";
    my @vl = UtilXml::GetTagData( $value, $tag1);
    print UtilXml::SaveTagData($vl[0], $tag1),"\n";
  }

close(INPUT);


Это сейчас так работает. А вот это хочу сделать для обучения:
Есть вышеприведенный XML файл. Сразу скажу: "Формирую его только я и никто другой, структура заранее известна и приведена в файле". smile DOM XML, XML::Simple не предлагать я тут учусь регулярным выражениям. smile
Так вот я хочу сделать вот так:
Открыл файл. 
Нашел первый тэг (целое слово внутри угловых скобок). 
Получил имя этого тэга $1. 
Нашел закрывающий тэг $3 (по полученному имени) и все что внутри $2. 
Перешел к обработке второго тэга, третьего и т.д. 
Меня интересует именно "академически" как это сделать внутри одного рег. выражения. Не хотелось бы сначала первым рег. выражением получать первый тэг, затем с помощью GetTagData (см. выше) получать все остальное. 

Автор: klem4 20.3.2009, 17:31
Вам нужно построить деревто чтоли ? Лучьше рекурсивную функцию написать мне кажется чем пытаться регуляркой это сделать. 

Что вы хотите получить регуляркой от такого xml:

Код

<a>
  <b>xx</b>
  <c>
     <d>1</d>
      <d>2</d>
  </c>
</a>


?

Автор: KSURi 20.3.2009, 17:57
AndreyCH, да, вы выбрали неверный метод для изучения регексов

Автор: AndreyCH 20.3.2009, 19:36
Народ, ну что вы в самом деле! ;-) smile  Я не очень ясно выразил свою мысль. В соседней ветке (Учусь ООП) уже все написано для того, чтобы моя функция вызывалась рекурсивно. Мне нужно по одному уровню дерева пробежаться в XML файле. smile Меня в приведенном примере не устраивает только то, что нужно "из вне" задавать тэг. Если я буду делать все это в регулярном выражении, будет "красивее".
Поясню. На первом проходе обработается только один тэг <user_list>  (он и является единственным элементом массива)во втором проходе обработаем уже только 
Код

  <user>
    <id>3</id> 
    <name>Sergey</name>
    <lname>Petrov</lname>
  </user>
  <list>
    <id>83</id> 
    <frame>back01</frame>
    <frame>back02</frame>
  </list>

соответсвенно все что внутри <user> добавляем в первый элемент нового массива, все что внутри <list> второй
и так далее обрабатываем внутрь. 
Теперь перефразирую вопрос.
Код

$val =~ m{<$tag>(.+?)</$tag>}gs;

где $tag - это собственно и есть название тэга. 
вот как мне написать регулярное выражение чтобы до этой операции находилось название тэга... А уж потом сделолсь тоже что и в приведенном примере. И все это внутри одного регэкспа.
PS а вот тут уже можно и регэкспам поучиться. smile

Автор: KSURi 20.3.2009, 20:03
Код

$val =~ /<(.+?)>(.+?)<\1>/

В $2 окажутся данные из тэга

Добавлено через 1 минуту и 54 секунды
Код

my(undef, $data) = $val =~ /<(.+?)>(.+?)<\1>/

Автор: AndreyCH 20.3.2009, 22:32
Ну и умучился я!!! Помощи и разъяснения душа просит!!! smile
итак. xml файли остался прежним - (смотри пред ответ)
Код

#!/usr/bin/perl
use strict;
open (INFILE, '<users.xml') or die 'cant open file';
undef $/;
my $val = <INFILE>;
my($data1, $data2) = $val =~ /<(.+?)>(.+?)<\1>/;
print $data1, " ", $data2, "\n";
close (INFILE);

и$data1 и$data2 undef и я код так мучил и по-другому и вот до чего дошел если меняем регулярку на
Код

my($data1) = $val =~ /<(.+?)>/;

на выходе имеем $data1 = "user_list"  как и должно быть. smile
теперь меняем на 
Код

my($data1, $data2) = $val =~ /<(.+?)>(.+?)/;

и оппа!
$data1 = "id" (это-то откуда взялось!!!)
$data2 = "3" (еще одно "чудо в решете")
Народ ну объясните что за... ? smile
Ну и наконец как толко беру  исходное - 
Код
my($data1, $data2) = $val =~ /<(.+?)>(.+?)<\1>/
сразу и$data1 и$data2 undef.
В чем же тут дело?

Автор: amg 21.3.2009, 10:08
Цитата(AndreyCH @  20.3.2009,  22:32 Найти цитируемый пост)
Ну и умучился я!!! Помощи и разъяснения душа просит!!! 
AndreyCH, для начала замените исходную регулярку на логичную для Вашего случая
/<(.+?)>(.+?)<\/\1>/s
и с ней уже экспериментируйте

Цитата(AndreyCH @  20.3.2009,  22:32 Найти цитируемый пост)
В чем же тут дело?
Строка то у Вас многострочная (поэтому нужен модификатор /s), и в закрывающем тэге есть слеш.

Автор: SkoobyDoo 21.3.2009, 13:31
AndreyCH,
Код

my($data1, $data2) = $val =~ /<(.+?)>(.+?)/;

Код

и оппа!
$data1 = "id" (это-то откуда взялось!!!)
$data2 = "3" (еще одно "чудо в решете")
Народ ну объясните что за... ? smile


регексп поменяйте на 
Код

my ($data1,$data2) = $val =~ m{<([^>]+)>(.+?)</\1>}s;

Автор: KSURi 22.3.2009, 00:11
Цитата(amg @  21.3.2009,  10:08 Найти цитируемый пост)
AndreyCH, для начала замените исходную регулярку на логичную для Вашего случая
/<(.+?)>(.+?)<\/\1>/s
и с ней уже экспериментируйте

упс) спасибо за поправку

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)