Распарсить файл (похож xml)


angel2s2 аватар

angel2s2 - Posted on 29 Декабрь 2008

Доброе время суток, коллеги.

Нужна ваша помощь, очень сильно.
До вечера надо распарсить файл. Содержание файла (отрывок):

<config>
<list name="User">
  <listitem>
    <variable name="Name">Administrator</variable>
    <variable name="Domain">domain.ru</variable>
    <variable name="Account_enabled">1</variable>
    <variable name="Auth_type">1</variable>
    <variable name="Password"></variable>
    <variable name="PIN"></variable>
    <variable name="Rights">0</variable>
    <variable name="ForwardMode">0</variable>
    <variable name="HomeServer"></variable>
    <variable name="MailboxLocation"></variable>
    <variable name="Qstorage">0</variable>
    <variable name="Qmessage">0</variable>
    <variable name="Fullname"></variable>
    <variable name="Description"></variable>
  </listitem>
  <listitem>
    <variable name="Name">admin</variable>
    <variable name="Domain">domain.ru</variable>
    <variable name="Account_enabled">0</variable>
    <variable name="Auth_type">0</variable>
    <variable name="Password">DE3:859040a78d562c970a16da52057897dfcc7feee</variable>
    <variable name="PIN"></variable>
    <variable name="Rights">0</variable>
    <variable name="ForwardMode">0</variable>
    <variable name="HomeServer"></variable>
    <variable name="MailboxLocation"></variable>
    <variable name="Qstorage">0</variable>
    <variable name="Qmessage">0</variable>
    <variable name="Fullname"></variable>
    <variable name="Description"></variable>
  </listitem>
</list>
</config>

Что нужно получить на выходе:

1;Administrator@domain.ru
0;admin@domain.ru

Как формируется:
Значения берутся из этих трех полей:

<variable name="Name">Administrator</variable>
<variable name="Domain">domain.ru</variable>
<variable name="Account_enabled">1</variable>

Т.е. "1" берется из поля "<variable name="Account_enabled">1</variable>", после него вставляется точка с запятой (;), потом из поля "<variable name="Name">Administrator</variable>" выдирается "Administrator", добавляется "@", a потом "domain.ru" из поля "<variable name="Domain">domain.ru</variable>".

Как их повыдирать по отдельности я знаю, но как соединить в одно целое... Для меня вопрос. Может есть оптимальное решение? Или может есть xml парсер, который можно из bash-скриптов изспользовать?

Заранее благодарен.

1. Ставишь тулзу xsltproc
2. Пишешь файлик, например transform.xsl, с таким содержмнием:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
 
  <xsl:template match="/config/list/listitem">
    <xsl:value-of select="variable[@name='Account_enabled']"/>;<xsl:value-of select="variable[@name='Name']"/>@<xsl:value-of select="variable[@name='Domain']"/>
  </xsl:template>
 
  <xsl:template match="/"><xsl:apply-templates/></xsl:template>
</xsl:stylesheet>

3. Скармливаешь все это тулзе:

xsltproc transform.xsl listusers.xml

4. ...
5. PROFIT! :)

Нужна ваша помощь, очень сильно.
надо распарсить файл. Содержание файла (отрывок): benchmark_run result='OK' binary_size='535942'

По этому отрывку не вполне понятно какую структуру имеет файл и что нужно из него извлечь. Покажите более крупный кусок и какой результат вы хотите получить. Пока что непонятно следующее:
1) Верно ли, что каждая строка имеет следующий вид:
benchmark_run result=некая_строка binary_size=некое_число
2) Может быть каждая строка имеет вид
benchmark_run некоторая_переменная=некоторое_значение ...
т.е. имена переменных заранее не известны?
3) Ну и опять же какой именно нужен результат?

Например, следующий скрипт что-то делает, но может быть это не то что надо:

while read line ; do
        eval $(echo $line | cut -d' ' -f2-)
        echo $result
        echo $binary_size
done

нужно парсинг benchmark_run result='OK' binary_size='535942'

надо распарсить файл. Содержание файла (отрывок)

<benchmark_run result='OK' binary_size='535942' binary_hash='cbbb304ef7'>

лучше поздно, чем никогда Ж:-)
шел программиног плохо предназначены для работы с xml. Изза этого в юниксах xml и не любят.
самое правильное конечно на каком нибуть скриптовом языком воспользоватся спец библиотекой для этого.

Но что бы просто продемонстрировать подход, набросал очень quick и очень dirty решение. Считается что;

  • все элементы идут последовательно
  • каждый элемент variable в одну строку как показано в примере

(это можно в одну строку)

cat parse.xml | sed -ne 's,.*<variable name="\([^"]*\)">\([^<]*\)</variable>.*,\U\1\E=\2,p' | 
    while read VAR; do
        eval $VAR;
        if [ "$ACCOUNT_ENABLED" ]; then
            echo "$ACCOUNT_ENABLED;$NAME@$DOMAIN";
            unset ACCOUNT_ENABLED;
        fi;
    done

sed преобразует все xml теги в строки вида NAME=Administrator, DOMAIN=domain.ru и так далее

В цикле построчно берутся строки и преобразуются в переменные.
Как только встречается переменная ACCOUNT_ENABLED, считается что NAME и DOMAIN
уже раньше было и выводится нужная строка. После чего переменная-триггер
ACCOUNT_ENABLED убирается.

Афигеть!!! А я мутил... :-( А оказывается всё просто так :-)

Респект тебе огромный.
В очередной раз убеждаюсь, что ты спец. :-)

#!/usr/bin/php
<?
    if (!@$argv[1]) exit ('You must provide file to process!' . "Like:\n" . basename($argv[0]) . " file_to_process\n");
 
preg_match_all('#<variable name="(?:Name|Domain|Account_enabled)">(.*?)</variable>#i', file_get_contents($argv[1]), $m, PREG_SET_ORDER);
 
    for ($i = 0; $i < count($m); $i+=3){
    echo $m[$i+2][1] . ';' . $m[$i][1] . '@' . $m[$i+1][1] . "\n";
    }
?>

Если уж парсить через php то не регуляркой а через DOMDocument

http://mymans.org/category/php/domdocument

http://ru2.php.net/simplexml не подойдет чтоли? Или DOMxml, libxml и другие в его составе ( http://ru2.php.net/manual/en/refs.xml.php )?

Естественно что так "правильнее" работать с XML, я же предложил самый "быстрый" способ слету, вот и все.

Сомневаюсь, что для CLI версии PHP такое прокатит... Тут ведь РНР не веб-сервером вызывается, а значит DOMDocument не прокатит (он же браузерный вроде как...)

--
Аффтар (из инеткафе пишу, региться не стал, т.к., по идее, машины (не знаю все или нет, но одна, которая мне всю флешку заразила, - 100%) инифицированы вирусом Virus.Win32.Sality.z (в терминалогии каспера),а его версия d перехватывала клаву и отправляла все по мылу куда-то :( .

С каких про DOMDocument стал браузерный? Вы скорее всего батенька путаете с DOM в javascript а это не одно и тоже

$a = '< a><b><c id="tit" name="titname">Page Title</c></b></a>';
$dom = new DOMDocument('1.0', 'utf-8');
$dom->loadXML('$a');

$mytag = $dom->getElementById('tit');
echo $mytag->get_attribute('name');
echo $mytag->nodeValue;
echo $mytag->nodeName;

и еще много удобного

во это самый кошерный способ.
аналогичные либы есть в любом скриптовом языке

не сомневайся Ж:-)
можно и через cli

МЕГА РЕСПЕКТ!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Все прошло на ура :))) Спасибо, огромное!!!! :)))))))))))

Можешь рассказать как этот скрипт работает? Я просто не знаю PHP :( Точнее учил когда-то, но уже не вспомню :(

Просто хочу на bash переписать его, чтобы всегда был под рукой :)
Соображения есть, но есть и сомнения ;)

Да могу конечно рассказать, но не посимвольно же разбирать его в самом деле?
Если есть вопросы - с удовольствием отвечу. Правда наверное лучше это уже все приватно - велкам в джаббер ко мне!

Ну а на Баш какой смцысл его переписывать - просто будет многословнее, вот и все...
Еще на AWK или PERL можно было бы понять конечно...

AWK? Что-то не складывается у меня в голове как такое сделать на нем... :-(
Примерчик может кто-нибудь подкинуть?

awk '{if(sub(/.*<variable name="/,"")&&sub(/<\/variable>/,"")){p=gensub(/">.*/,"","");v=gensub(/.*">/,"","");h[p]=v;if(p == "Account_enabled") print v";"h["Name"]"@"h["Domain"]}}' parse.xml

или более красиво отформатированое.

awk '{
        if(sub(/.*<variable name="/,"") && sub(/<\/variable>/,""))
        {
            p=gensub(/">.*/,"","");
            v=gensub(/.*">/,"","");
            h[p]=v;
            if(p == "Account_enabled")
                print v";"h["Name"]"@"h["Domain"]
        }
    }' parse.xml

основная проблема что в регеспах не поддерживается выделение регионов в с помощью ()

Ну зачем же так сложно и вправду?

#!/usr/bin/awk -f
BEGIN{
RS="[[:space:]]*</?listitem>[[:space:]]*"
FS="[[:space:]]*<variable name=\"|\">|</variable>\n+"
}
/variable/{
print $9 ";" $3 "@" $6
}

Можете как таким скриптом, так и в одну строчку прямо из Баша (не красиво):
awk 'BEGIN{RS="[[:space:]]*</?listitem>[[:space:]]*";FS="[[:space:]]*<variable name=\"|\">|</variable>\n+"} /variable/{print $9 ";" $3 "@" $6;}' config.xml

И на счет "что в регеспах не поддерживается выделение регионов в с помощью ()":
1) С чего вы взяли???
Вы вообще МАН по нему читали?:

       gensub(r, s, h [, t])   Search the target string t for matches of the regular expression r.  If h is a string beginning  with  g  or  G,
                               then replace all matches of r with s.  Otherwise, h is a number indicating which match of r to replace.  If t is
                               not supplied, $0 is used instead.  Within the replacement text s, the sequence \n, where n is a digit from 1  to
                               9,  may  be  used  to indicate just the text that matched the n’th parenthesized subexpression.  The sequence \0
                               represents the entire matched text, as does the character &.  Unlike sub() and gsub(), the  modified  string  is
                               returned as the result of the function, and the original target string is not changed.
 
       gsub(r, s [, t])        For  each  substring  matching the regular expression r in the string t, substitute the string s, and return the
                               number of substitutions.  If t is not supplied, use $0.  An & in the replacement text is replaced with the  text
                               that  was  actually  matched.  Use \& to get a literal &.  (This must be typed as "\\&"; see GAWK: Effective AWK
                               Programming for a fuller discussion of the rules for &’s and backslashes  in  the  replacement  text  of  sub(),
                               gsub(), and gensub().)

Так что поддерживаются какраз ссылки \\n и $n
2) Наверное это все же главное - тут все же надо с полями работать и стандартной логикой AWK.

Интересно, такими пафосными рождаются или это приобретается по мере приобритения навыков? Ж:-))
Восклицать "зачем так сложно" и давать довольно таки не очевидное решение - это что то Ж:-)
Это мозги надо на изнанку вывернуть что бы представить xml в виде полей. Вот у меня так не получилось. У ВАС вышло. За что вам честь и хвала.
И ман я читал. Странно да? И если я где то чего не то не углядел, это не повод патетично закатывать глаза. Можно подумать ВАШИ решения идеальны и ВЫ никогда не ошибаетесь.

Про gensub() дейсвительно проглядел. Вот так лучше

awk '/variable/{$0=gensub(/.*="([^"]*)">([^<]*).*/,"\\1 \\2","");h[$1]=$2;if($1 == "Account_enabled") print $2";"h["Name"]"@"h["Domain"]}' parse.xml

() я перепутал с {}. Что бы они работали нужно указывать флажок специально. "чув звiн, та забув де вiн".

Насчет работы через поля в awk таки да надо думать в первую очередь.

Ладно, ладно, не лучший был у меня тон, согласен, прошу прощения, но Вы ответили не менее пафосно... предлагаю мир. :)

Теперь по делу
1) О неочевидности решения - с удовольствием объясню что что не понятно или вызывает вопросы. Это раз. Ну и два, это то что какраз по идеологии AWK - задается разбиение на записи и поля (records & fields), так что по идее должно быть какраз на много очевиднее, потому что вся работа делается в АWK, и как видно, у меня никакого программинга и разбора нету вовсе больше, один единственный print.
2) На счет того что XML, разумеется надо парсить не так, а с помощью его парсеров (см обсуждение этого для ПХП сдесь же), я уже говорил что полностью согласен. На сколько я знаю, AWK этого не умеет, да и в принципе предназначен не для этого.
3) Что проглядели в МАНе не беда, хорошо что хоть не спорите. Рад что было полезно все-таки замечание. Единственное, стоит все же быть менее категоричным в заявлениях... Сам конечно ошибаюсь! Не ошибается тот, кто ничего не делает.
4) О каком флажке речь? Для того чтобы ссылки вида \\n работали? И Вы на какой системе пробовали? Я использую gawk на Линуксе, и все работает по умолчанию...

мир. забыли.
1. Вопросов нет. Я говорил только о том что лично мне идея работать с xml как с полями используя теги как разделители не очевидна. Так как могут и не так идти, и лишние теги быть и все такое.
2. К этому тоже небыло претензий.
3. Зачем же мне спорить с очевидным? Ж:-) Категаричность была вызвана разражением от общего тона письма.
4. раздел Regular Expressions

       r{n,m}     One or two numbers inside braces denote an interval
       expression.  If there is one number in the braces, the preceding regular
       expression r is repeated n times.  If there are two numbers separated by
       a comma, r is repeated n to m times.  If there is one number  followed
       by  a comma, then r is repeated at least n times.  Interval expressions
       are only available if either --posix or --re-interval is specified on
       the command line.

говорю ж () и {} спутал.
в общем все понятно, как по мне разобрались

Класс!!! :) Спасибо большое, теперь буду знать :)

=) Сенкс. По символам не нужно :))) Просто парочка строк не понятны.

А где я твой яббер найду? ;) тут его вроде как нету...
Стукнись в асю ко мне (334-714-695), правда я буду в сети уже наверное после 10 января 2009. :)

Упссс, был уверен что джаббер мой есть здесь в профиле, пардонки:

И стучитесь Вы ко мне лучше уж все же. И аську в топку тоже, проприетарная она...

Ок. 11 выйду на работу, стукну, дома нета нету, с мобилы порой лазаю :-(

О парни вы жжёте, я тоже так хочу скрипты на баше штопать, только вот мат. Р.В. не как не стекаются с этими :,(

что такое "мат. Р.В."?

Отправить комментарий

Google Friend Connect (leave a quick comment)
loading...
Содержание этого поля является приватным и не предназначено к показу.