![]() |
парсинг xml :)
Суть:
Имеем xml файлы с содержимым вида: в данном случае представлены номер, координаты, погрешность. нас интересуют параметры Su_Nmb="хх" и Ord_Nmb="хх" параметр Ord_Nmb находится в той же строке что и все остальные значения и именно он является необходимым (а не Su_Nmb) обрабатывалось все кодом: Код:
For /F "Tokens=2-6 Delims= " %%a In ('C:\WINDOWS\system32\find.exe "Ordinate" ^<"%file_name%"') Do echo %%a %%b %%c %%e >> temp.txt но сейчас изменился формат xml файла и нужный номер точки записывается теперь в параметр Su_Nmb. вопрос: каким образом можно вытащить и связать в правильном соответствии номер точки и координаты, если они в разных строках (но строго друг за другом) ? второй вопрос: можно ли сразу вытаскивать лишь значения (вместо x="515401.19" сразу 515401.19) чтобы иметь возможность записывать в csv |
Цитата:
Цитата:
|
Цитата:
информация идет блоками вида: что нужно: из строки <Parcel CadastralNumber="59:23:1271002:40" взять значение (собственно 59:23:1271002:40) далее нужная информация идет по 2 строки <Spelement_Unit Type_Unit="Точка" Su_Nmb="1"> тут интересно значение из Su_Nmb= и следующая за ней <Ordinate X="334917.30" Y="2174613.89" Ord_Nmb="1" Delta_Geopoint="1.50" /> тут берем X= Y= Delta_Geopoint= в идеале имеем csv файл (если рассматривать кусок xml выше) вида: 59:23:1271002:40; 1; 334917.30; 2174613.89; 1.50 59:23:1271002:40; 2; 334866.75; 2174615.41; 1.50 59:23:1271002:40; 3; 334868.87; 2174621.38; 1.50 59:23:1271002:40; 4; 334918.72; 2174619.85; 1.50 и т.д. с другим блоком <Parcel> |
Попробуйте так (PoSH):
Код:
$sSourceFile = "C:\Песочница\041\КПТ_2014-01-24.xml" |
Iska, благодарю, данный вид xml обрабатывает.
проблема в том, что я не владею powershell. Могли бы Вы снабдить каждую строку комментарием ? был бы весьма признателен, так как есть еще несколько типов xml документов и мне было бы совестно каждый раз просить о помощи :) всего 5 видов документов и не регулярно меняется схема документа (на данный момент уже 8). вот именно из-за изменения схемы и потребовалась помощь.. |
Код:
# Путь к файлу XML Код:
# От текущего узла «Parcel» опять «спускаемся» вниз по иерархии одиночных узлов Код:
# Строим результирующую объект-строку из переменной «$sCadastralNumber», Код:
} Цитата:
|
firstarey, если вас всё ещё интересует вариант на cmd, вот он:
Код:
@Echo Off |
Iska, Искренне Вам благодарен. Комментарии в сопровождении иллюстраций весьма емко объясняют работу PowerShell с xml.
Пожалуй попробую переписать свою монстро-cmd на powerShell. если есть интерес могу выложить то, как это работало с примерами. Anonymоus, благодарю что откликнулись, но у меня данный вариант не захотел обрабатывать xml (создает пустой файл csv и пишет All done). попробовал на нескольких xml. как написал выше попробую переписать все под PowerShell - он значительно легче работает с xml :) |
firstarey, на вашем xml из этого сообщения у меня (Win7 x64) всё отработало, я протестировал перед тем, как постить. В голову приходит разве что проблема с кириллицей в имени файла, если вы сохранили скрипт не в 866 кодировке.
UPD: запустил на виртуалке (XP SP3 x86) - действительно, не заработало. Моя ошибка, недостаточно тщательно протестировал. Исправил сообщение со скриптом. |
Anonymоus, благодарю.
моих навыков работы с командной строкой не хватает для понимания работы некоторых моментов. буду рад если Вы откликнитесь. во первых: Код:
:: Читаем документ поблочно 1. берем строку целиком 2. присваиваем в переменную data (почему в кавычках??) 3. если переменная block равна true если нашли строку начинающуюся на </Parcel (т.е. тут сначала выполняется второе if ? если выполнилось второе if, то переменной Block присвоить true?) 4. в переменную block записать значение false 5. вызываем blockData из цикла 6. пропускаем строку (так понимаю для наглядности отображения при выполнении, чтобы разбить блоки пустой строкой ?) 7. не понял эту строку.. в цикле берем первую подстроку после "=" из..(что делает ('Set BlockData_')??) и далее для меня тоже не понятно что выполняется в Do (Set %%B=) Код:
:BlockData 1. как это работает и что нам дает? For %%A In (n d) Do Set %%A=0 2. добавить к переменной n единицу ? 3. если существует строка n ( 4. если не существует строка n:Su_Nmb ... как это работает ? 5. в переменную d записать значение строки+(как cmd поймет Shift??) 6. вызываем GetData с параметром "номер строки" "Su_Nmb" 7. вызываем GetData с параметром "номер строки+shift" "Ordinate" 8. все еще внутри цикла выводим результат на экран (!%KeyWord%! - и знак "!" и "%" ?) 9. выводим найденное в файл результата 10. если не нашли нужную строку, то выход ? 11. повторили поиск нужной строки.. Код:
For /F "tokens=1 delims=<>" %%? In ("%~1") Do ( 2. присвоить переменной data.. а что ей присвоить ? 3. проверку тоже не понял. как сработает проверка значения ? переменная без первых двух значений проверяется на равенство самой себе 4. set data тоже не понял.. 5. опять не понял.. Боюсь Ваше решение моего вопроса принесло еще больше вопросов:) на самом деле мне действительно интересно понять как работает Ваша конструкция и я надеюсь Вы прокомментируете более подробно описанные моменты. |
Цитата:
2. Пишем её в Data (в кавычках - чтобы избежать неожиданных и неприятных ситуаций с необходимостью экранировать спецсимволы, из которых в XML в наличии как минимум, операторы перенаправления '>' и '<'). Чтобы было понятнее, наглядный пример: Set Test=ab>cd запишет в Test лишь "ab" и создаст пустой файл "cd", в то же время Set "Test=ab>cd" даст в качестве значения переменной именно то, что нужно. 3-7. Здесь всё идет немного не в том порядке, в котором оно исполняется. Опишу построчно, в порядке исполнения. Строка 29: Проверяем на соответствие первых восьми символов текущей строки началу логического блока, т.е. открывающего тэга "<Parcel ". Если есть совпадение, устанавливаем счетчик i в ноль и помечаем, что мы внутри блока: Set Block=true Там же читаем из открывающего тэга искомый номер CadastralNumber, вызывая одну из функций. Строка 37: Двойной IF, если это не открывающий тэг "<Parcel " И мы внутри блока (установлен флаг), инкрементируется значение i (счетчик строк) и соответствующая строка XML пишется в переменную BlockData_номер_строки. Строка 21: Если мы внутри блока и текущая строка = закрывающий тэг "</Parcel", сбрасываем пометку "внутри блока" и переходим к собственно, обработке данных (вызов функции Call :BlockData, которая работает с псевдомассивом из переменных BlockData_номер). Там же выводится пустая строка - просто визуальный разделитель, добавил для наглядности. Строка 26: После обработки данных удаляем значения всех переменных BlockData_номер. Для этого в цикле выводим все переменные (Set имя_переменной или часть имени выведет все переменные, с именем, подходящим под заданную строку в формате имя=значение), берем первую часть до = (имя) и удаляем их (назначаем пустое значение) Всё, с основной частью закончили, осталось разобрать функции. Цитата:
2. Инкрементируем n на единицу, так как изначально оно у нас было установлено в 0, а нумерация строк в блоке данных идет с единицы. Далее за каждый проход идёт такая же инкрементация на единицу. По сути, это "велосипедная" реализация бесконечного цикла (т.к. мы не знаем заранее, сколько у нас строк в блоке, а For /L %%? In (0,0,0) некорректно отрабатывает в данном случае). Можно бы было "подсмотреть" значение i, но на тот момент мне это не пришло в голову. 3. Если существует переменная BlockData_номер (она не пустая), то переходим к действиям с ней, иначе - выходим из функции. 4. Ищем строку с номером, так как она является отправной точкой, мы ищем именно её + следующую. Поиск осуществляется путём проверки (не)равенства "строка минус ключевое слово"=="строка". Ключевое слово у нас "Su_Nmb". Проверка равенства работает гораздо быстрее, чем вызов Find/FindStr 5. Когда строка найдена, допустим, она в переменной BlockData_3, нам надо определить и следующую, для этого используем заданный заранее сдвиг, в этом случае - одна строка. Если структура XML поменяется, не составит труда изменить нужный параметр. Т.е. к известному нам номеру строки прибавляем сдвиг и знаем, что координаты будут лежать в BlockData_4. Shift - назначенная в начале скрипта переменная, в конструкции Set /A она автоматически развернется в своё значение. 6. Дважды вызываем функцию для получения данных из обеих строк, в качестве параметра указываем само тело строки и ключевое слово для проверки. Оно служит для контроля - не изменилась ли структура? Если там будет что-то другое вместо ожидаемого, мы получим сообщение о ошибке парсинга XML. 7. Выводим полученные данные дважды - на экран, для визуального контроля, и дописываем в файл с результатом. "(!%KeyWord%! - и знак "!" и "%" ?)" - сначала мы через % разворачиваем KeyWord в Su_Nmb, а затем, через ! получаем значение Su_Nmb. Цитата:
2. Присваиваем Data переданный функции первый параметр, который мы строкой выше очистили от двойных кавычек и угловых скобок. 3. Опять тот же трюк с проверкой равенства. Если в строке не найдено ключевое слово, переданное функции вторым параметром, следовательно в строке не то, что мы ожидали, выводим ошибку парсинга. Для строки с номером точки это будет Su_Nmb, а для строки с координатами, соответственно, Ordinate 4. Если всё верно, проводим ряд манипуляций со строкой. а) Нам нужно убрать обрамляющие кавычки, в которые заключены значения координат b) Обрамляем переменную двойными кавычками по бокам и заменяем каждый пробел на кавычка+пробел+кавычка. В результате из Код:
Ordinate X="334527.77" Y="2175865.20" Ord_Nmb="1" Geopoint_Zacrep="626003000000" Delta_Geopoint="0.10" / Код:
"Ordinate" "X=334527.77" "Y=2175865.20" "Ord_Nmb=1" "Geopoint_Zacrep=626003000000" "Delta_Geopoint=0.10" "/" Код:
Ordinate Да, по сути это куча велосипедных реализаций и говнокода, но, к сожалению, cmd не имеет нативных инструментов для работы со структурированными данными вроде XML. Реализация на PowerShell, которую написал товарищ Iska, гораздо быстрее и проще, но и cmd пока рано списывать со счетов. |
Время: 21:05. |
Время: 21:05.
© OSzone.net 2001-