|
Компьютерный форум OSzone.net » Программирование, базы данных и автоматизация действий » Скриптовые языки администрирования Windows » CMD/BAT - [решено] парсинг xml :) |
|
|
CMD/BAT - [решено] парсинг xml :)
|
Пользователь Сообщения: 102 |
Суть:
Имеем 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 |
|
Отправлено: 09:46, 05-03-2014 |
Ветеран Сообщения: 27449
|
Профиль | Отправить PM | Цитировать Цитата firstarey:
Цитата firstarey:
|
||
Отправлено: 09:51, 05-03-2014 | #2 |
Для отключения данного рекламного блока вам необходимо зарегистрироваться или войти с учетной записью социальной сети. Если же вы забыли свой пароль на форуме, то воспользуйтесь данной ссылкой для восстановления пароля. |
Пользователь Сообщения: 102
|
Профиль | Отправить PM | Цитировать Цитата Iska:
информация идет блоками вида: что нужно: из строки <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> |
|
Последний раз редактировалось firstarey, 16-05-2014 в 06:13. Отправлено: 12:26, 05-03-2014 | #3 |
Ветеран Сообщения: 27449
|
Профиль | Отправить PM | Цитировать Попробуйте так (PoSH):
$sSourceFile = "C:\Песочница\041\КПТ_2014-01-24.xml" $oXmlDocument = New-Object -TypeName System.Xml.XmlDocument $oXmlDocument.load($sSourceFile) $oXmlDocument.Region_Cadastr.Package.Cadastral_Blocks.Cadastral_Block.Parcels.Parcel |` ForEach-Object -Process { $sCadastralNumber = $_.CadastralNumber $_.Entity_Spatial.Spatial_Element.Spelement_Unit |` ForEach-Object -Process { "$sCadastralNumber;$($_.Su_Nmb);$($_.Ordinate.X);$($_.Ordinate.Y);$($_.Ordinate.Delta_Geopoint)" } } | Out-File -FilePath "C:\Песочница\041\0001.csv" -Encoding ascii |
Отправлено: 14:10, 05-03-2014 | #4 |
Пользователь Сообщения: 102
|
Профиль | Отправить PM | Цитировать Iska, благодарю, данный вид xml обрабатывает.
проблема в том, что я не владею powershell. Могли бы Вы снабдить каждую строку комментарием ? был бы весьма признателен, так как есть еще несколько типов xml документов и мне было бы совестно каждый раз просить о помощи ![]() всего 5 видов документов и не регулярно меняется схема документа (на данный момент уже 8). вот именно из-за изменения схемы и потребовалась помощь.. |
|
Последний раз редактировалось firstarey, 16-05-2014 в 06:13. Отправлено: 07:24, 06-03-2014 | #5 |
Ветеран Сообщения: 27449
|
Профиль | Отправить PM | Цитировать # Путь к файлу XML $sSourceFile = "C:\Песочница\041\КПТ_2014-01-24.xml" # Создаём экземпляр объекта типа «System.Xml.XmlDocument» $oXmlDocument = New-Object -TypeName System.Xml.XmlDocument # Загружаем содержимое файла в созданный экземпляр объекта $oXmlDocument.load($sSourceFile) # PowerShell умеет представлять дочерние узлы xml в виде свойств, ординалом или массивом # (равно, как и атрибуты узла), потому мы можем просто обращаться к свойствам/атрибутам узла через точку. # # Для одиночного узла, свойство — ординал, мы просто «спускаемся» вниз по иерархии одиночных узлов. # Для множества одинаковых узлов, свойство — массив, мы используем перечисление, # передавая полученный массив узлов по конвейеру на разбор командлету «ForEach-Object». $oXmlDocument.Region_Cadastr.Package.Cadastral_Blocks.Cadastral_Block.Parcels.Parcel |` ForEach-Object -Process { # «$_» — текущий экземпляр каждого из элементов массива, переданного по конвейеру # командлету «ForEach-Object». Запоминаем атрибут «CadastralNumber» текущего узла «Parcel» # в переменной «$sCadastralNumber»: $sCadastralNumber = $_.CadastralNumber # От текущего узла «Parcel» опять «спускаемся» вниз по иерархии одиночных узлов # до очередного массива узлов, который так же передаём по конвейеру командлету «ForEach-Object». $_.Entity_Spatial.Spatial_Element.Spelement_Unit |` ForEach-Object -Process { # Строим результирующую объект-строку из переменной «$sCadastralNumber», # атрибутов текущего узла «Spelement_Unit» и # атрибутов «X», «Y» и «Delta_Geopoint» дочернего для него узла «Ordinate»: "$sCadastralNumber;$($_.Su_Nmb);$($_.Ordinate.X);$($_.Ordinate.Y);$($_.Ordinate.Delta_Geopoint)" } } | Out-File -FilePath "C:\Песочница\041\0001.csv" -Encoding ascii # ↑↑ Набор результирующих строк передаём по конвейеру командлету «Out-File», осуществляющему вывод в файл. ↑↑ Цитата firstarey:
|
|
Отправлено: 22:00, 06-03-2014 | #6 |
Старожил Сообщения: 415
|
Профиль | Отправить PM | Цитировать firstarey, если вас всё ещё интересует вариант на cmd, вот он:
@Echo Off SetLocal EnableDelayedExpansion :: Inquisitor, 2014 :: Ad majorem Applejack gloriam :: Файл для обработки Set XMLFile=fff.xml :: Ключевое слово по которому ищем строки с номерами точек Set KeyWord=Su_Nmb :: Смещение координат в строках от номера точки Set Shift=1 :: Файл для вывода результата обработки Set Result=output.csv :: Создаем (или перезаписываем) файл с результатом Echo.>"%Result%" :: Читаем документ поблочно For /F "usebackq eol= tokens=* delims=" %%A In ("%XMLFile%") Do ( Set "Data=%%A" :: Работа с содержимым блока If "!Block!"=="true" If "!Data:~,8!"=="</Parcel" ( Set Block=false :: Блок - обработка Call :BlockData Echo. :: Блок - удаление For /F "tokens=1 delims==" %%B In ('Set BlockData_') Do (Set %%B=) ) :: Определение начала блока If "!Data:~,8!"=="<Parcel " ( Set Block=true Set i=0 :: Чтение данных из заголовка Call :GetData "%%A" "CadastralNumber" ) :: Запись содержимого блока If Not "!Data:~,8!"=="<Parcel " If "!Block!"=="true" ( Set /A i+=1 Set "BlockData_!i!=!Data!" ) ) Echo All done Pause&Exit :BlockData For %%A In (n d) Do Set %%A=0 :jmp Set /A n+=1 :: Выбираем строки, содержащие номер точки If Defined BlockData_%n% ( If Not "!BlockData_%n%:%KeyWord%=!"=="!BlockData_%n%!" ( Set /A d=n+Shift :: Получаем номер точки и координаты Call :GetData "%%BlockData_!n!%%" "%KeyWord%" Call :GetData "%%BlockData_!d!%%" "Ordinate" :: Выводим сами данные в нужном формате Echo !CadastralNumber!; !%KeyWord%!; !X!; !Y!; !Delta_Geopoint! Echo !CadastralNumber!; !%KeyWord%!; !X!; !Y!; !Delta_Geopoint!>>"%Result%" ) ) Else Exit /B GoTo :jmp :GetData [1 - строка с данными, 2 - проверочное слово (не обязательно)] For /F "tokens=1 delims=<>" %%? In ("%~1") Do ( Set "Data=%%?" :: Проверка на соответствие ожидаемой структуре If "!Data:%~2=!"=="!Data!" (Echo XML parsing error & Exit /B 1) :: Подготавливаем строку к разбору Set "Data=!Data:"=!" Set "Data="!Data: =" "!"" :: Разбираем данные в цикле, читая пары ключ=значение For %%A In (!Data!) Do (For /F "tokens=1,* delims==" %%B In ("%%~A") Do (Set "%%~B=%%~C")) ) Exit /B |
Последний раз редактировалось Anonymоus, 07-03-2014 в 09:07. Причина: исправлено Отправлено: 02:19, 07-03-2014 | #7 |
Пользователь Сообщения: 102
|
Профиль | Отправить PM | Цитировать Iska, Искренне Вам благодарен. Комментарии в сопровождении иллюстраций весьма емко объясняют работу PowerShell с xml.
Пожалуй попробую переписать свою монстро-cmd на powerShell. если есть интерес могу выложить то, как это работало с примерами. Anonymоus, благодарю что откликнулись, но у меня данный вариант не захотел обрабатывать xml (создает пустой файл csv и пишет All done). попробовал на нескольких xml. как написал выше попробую переписать все под PowerShell - он значительно легче работает с xml ![]() |
Отправлено: 07:11, 07-03-2014 | #8 |
Старожил Сообщения: 415
|
Профиль | Отправить PM | Цитировать firstarey, на вашем xml из этого сообщения у меня (Win7 x64) всё отработало, я протестировал перед тем, как постить. В голову приходит разве что проблема с кириллицей в имени файла, если вы сохранили скрипт не в 866 кодировке.
UPD: запустил на виртуалке (XP SP3 x86) - действительно, не заработало. Моя ошибка, недостаточно тщательно протестировал. Исправил сообщение со скриптом. |
Последний раз редактировалось Anonymоus, 07-03-2014 в 09:09. Отправлено: 08:49, 07-03-2014 | #9 |
Пользователь Сообщения: 102
|
Профиль | Отправить PM | Цитировать Anonymоus, благодарю.
моих навыков работы с командной строкой не хватает для понимания работы некоторых моментов. буду рад если Вы откликнитесь. во первых: :: Читаем документ поблочно For /F "usebackq eol= tokens=* delims=" %%A In ("%XMLFile%") Do ( Set "Data=%%A" :: Работа с содержимым блока If "!Block!"=="true" If "!Data:~,8!"=="</Parcel" ( Set Block=false :: Блок - обработка Call :BlockData Echo. :: Блок - удаление For /F "tokens=1 delims==" %%B In ('Set BlockData_') Do (Set %%B=) ) ![]() 1. берем строку целиком 2. присваиваем в переменную data (почему в кавычках??) 3. если переменная block равна true если нашли строку начинающуюся на </Parcel (т.е. тут сначала выполняется второе if ? если выполнилось второе if, то переменной Block присвоить true?) 4. в переменную block записать значение false 5. вызываем blockData из цикла 6. пропускаем строку (так понимаю для наглядности отображения при выполнении, чтобы разбить блоки пустой строкой ?) 7. не понял эту строку.. в цикле берем первую подстроку после "=" из..(что делает ('Set BlockData_')??) и далее для меня тоже не понятно что выполняется в Do (Set %%B ![]() :BlockData For %%A In (n d) Do Set %%A=0 :jmp Set /A n+=1 :: Выбираем строки, содержащие номер точки If Defined BlockData_%n% ( If Not "!BlockData_%n%:%KeyWord%=!"=="!BlockData_%n%!" ( Set /A d=n+Shift :: Получаем номер точки и координаты Call :GetData "%%BlockData_!n!%%" "%KeyWord%" Call :GetData "%%BlockData_!d!%%" "Ordinate" :: Выводим сами данные в нужном формате Echo !CadastralNumber!; !%KeyWord%!; !X!; !Y!; !Delta_Geopoint! Echo !CadastralNumber!; !%KeyWord%!; !X!; !Y!; !Delta_Geopoint!>>"%Result%" ) ) Else Exit /B GoTo :jmp 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 ( Set "Data=%%?" :: Проверка на соответствие ожидаемой структуре If "!Data:%~2=!"=="!Data!" (Echo XML parsing error & Exit /B 1) :: Подготавливаем строку к разбору Set "Data=!Data:"=!" Set "Data="!Data: =" "!"" :: Разбираем данные в цикле, читая пары ключ=значение For %%A In (!Data!) Do (For /F "tokens=1,* delims==" %%B In ("%%~A") Do (Set "%%~B=%%~C")) ) 2. присвоить переменной data.. а что ей присвоить ? 3. проверку тоже не понял. как сработает проверка значения ? переменная без первых двух значений проверяется на равенство самой себе 4. set data тоже не понял.. 5. опять не понял.. Боюсь Ваше решение моего вопроса принесло еще больше вопросов ![]() на самом деле мне действительно интересно понять как работает Ваша конструкция и я надеюсь Вы прокомментируете более подробно описанные моменты. |
Отправлено: 11:46, 07-03-2014 | #10 |
|
![]() |
Участник сейчас на форуме |
![]() |
Участник вне форума |
![]() |
Автор темы |
![]() |
Сообщение прикреплено |
| |||||
Название темы | Автор | Информация о форуме | Ответов | Последнее сообщение | |
CMD/BAT - Парсинг XML/TXT | mxm199 | Скриптовые языки администрирования Windows | 10 | 17-07-2012 12:34 | |
PowerShell - [решено] парсинг XML файла | dosperados | Скриптовые языки администрирования Windows | 2 | 02-07-2012 21:02 | |
7 / 2008 R2 - dot1x + xml profile + unattend.xml | HaeMHuK | Автоматическая установка Windows 11 / 10 / 8 / 7 / Vista | 0 | 20-10-2011 12:43 | |
CMD/BAT - Парсинг | Чин Хон | Скриптовые языки администрирования Windows | 1 | 04-02-2011 13:51 | |
[решено] проверка XML-файла на соответствие XML schema в IE 6 и Firefox 2 | dimait | Вебмастеру | 4 | 23-08-2007 02:02 |
|