Имя пользователя:
Пароль:  
Помощь | Регистрация | Забыли пароль?  | Правила  

Компьютерный форум OSzone.net » Программирование, базы данных и автоматизация действий » Скриптовые языки администрирования Windows » CMD/BAT - [решено] парсинг xml :)

Ответить
Настройки темы
CMD/BAT - [решено] парсинг xml :)

Пользователь


Сообщения: 102
Благодарности: 8

Профиль | Отправить PM | Цитировать


Суть:
Имеем xml файлы с содержимым вида:
читать дальше »
<Spelement_Unit Type_Unit="Точка" Su_Nmb="1">
<Ordinate X="515401.19" Y="2232958.98" Ord_Nmb="1" Num_Geopoint="182" Delta_Geopoint="0.10" />
</Spelement_Unit>
<Spelement_Unit Type_Unit="Точка" Su_Nmb="2">
<Ordinate X="515411.18" Y="2232958.64" Ord_Nmb="19" Num_Geopoint="183" Delta_Geopoint="0.10" />
</Spelement_Unit>
<Spelement_Unit Type_Unit="Точка" Su_Nmb="3">
<Ordinate X="515414.92" Y="2232958.50" Ord_Nmb="18" Num_Geopoint="184" Delta_Geopoint="0.10" />
</Spelement_Unit>
<Spelement_Unit Type_Unit="Точка" Su_Nmb="4">
<Ordinate X="515439.74" Y="2232960.55" Ord_Nmb="17" Num_Geopoint="185" Delta_Geopoint="0.10" />
</Spelement_Unit>
<Spelement_Unit Type_Unit="Точка" Su_Nmb="5">
<Ordinate X="515449.79" Y="2232961.65" Ord_Nmb="16" Num_Geopoint="239" Delta_Geopoint="0.30" />
</Spelement_Unit>
<Spelement_Unit Type_Unit="Точка" Su_Nmb="6">
<Ordinate X="515450.43" Y="2232970.92" Ord_Nmb="15" Num_Geopoint="240" Delta_Geopoint="0.30" />


в данном случае представлены номер, координаты, погрешность.
нас интересуют параметры
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
получаем результат вида:
читать дальше »
X="515401.19" Y="2232958.98" Ord_Nmb="1" Delta_Geopoint="0.10"


но сейчас изменился формат xml файла и нужный номер точки записывается теперь в параметр Su_Nmb.

вопрос:
каким образом можно вытащить и связать в правильном соответствии номер точки и координаты, если они в разных строках (но строго друг за другом) ?
второй вопрос:
можно ли сразу вытаскивать лишь значения (вместо x="515401.19" сразу 515401.19) чтобы иметь возможность записывать в csv

Отправлено: 09:46, 05-03-2014

 

Ветеран


Сообщения: 27449
Благодарности: 8086

Профиль | Отправить PM | Цитировать


Цитата firstarey:
каким образом можно вытащить и связать в правильном соответствии номер точки и координаты, если они в разных строках »
Перейти к использованию правильных WSH/PoSH.

Цитата firstarey:
можно ли сразу вытаскивать лишь значения (вместо x="515401.19" сразу 515401.19) чтобы иметь возможность записывать в csv »
Всё можно. Если Вы упакуете образец Вашего xml в архив, который приложите к сообщению, и точно укажете, что потребно извлечь и как именно записать.

Отправлено: 09:51, 05-03-2014 | #2



Для отключения данного рекламного блока вам необходимо зарегистрироваться или войти с учетной записью социальной сети.

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


Пользователь


Сообщения: 102
Благодарности: 8

Профиль | Отправить PM | Цитировать


Цитата Iska:
образец Вашего xml в архив »
вложение КПТ_2014-01-24.zip

информация идет блоками вида:
читать дальше »
<Parcel CadastralNumber="59:23:1271002:40" Name="03" State="01" DateCreated="1999-11-19">
<Area>
<Area>302.10</Area>
<Unit>055</Unit>
</Area>
<Location>
<Address>
<Code_OKATO>57228000000</Code_OKATO>
<Code_KLADR>5901100000000</Code_KLADR>
<Region>59</Region>
<District Name="Куединский" Type="р-н" />
<Note>край Пермский, р-н Куединский, Гондыревское месторождение нефти</Note>
</Address>
</Location>
<Category Category="003003000000" />
<Utilization Kind="143001000000" ByDoc="для разработки и эксплуатации месторождений нефти и газа" />
<Unified_Land_Unit>
<Preceding_Land_Unit>59:23:0000000:22</Preceding_Land_Unit>
</Unified_Land_Unit>
<CadastralCost Value="109.66" Unit="383" />
<Entity_Spatial Ent_Sys="2">
<Spatial_Element>
<Spelement_Unit Type_Unit="Точка" Su_Nmb="1">
<Ordinate X="334917.30" Y="2174613.89" Ord_Nmb="1" Delta_Geopoint="1.50" />
</Spelement_Unit>
<Spelement_Unit Type_Unit="Точка" Su_Nmb="2">
<Ordinate X="334866.75" Y="2174615.41" Ord_Nmb="1" Delta_Geopoint="1.50" />
</Spelement_Unit>
<Spelement_Unit Type_Unit="Точка" Su_Nmb="3">
<Ordinate X="334868.87" Y="2174621.38" Ord_Nmb="1" Delta_Geopoint="1.50" />
</Spelement_Unit>
<Spelement_Unit Type_Unit="Точка" Su_Nmb="4">
<Ordinate X="334918.72" Y="2174619.85" Ord_Nmb="1" Delta_Geopoint="1.50" />
</Spelement_Unit>
<Spelement_Unit Type_Unit="Точка" Su_Nmb="1">
<Ordinate X="334917.30" Y="2174613.89" Ord_Nmb="1" Delta_Geopoint="1.50" />
</Spelement_Unit>
</Spatial_Element>
</Entity_Spatial>
</Parcel>


что нужно:
из строки <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
Благодарности: 8086

Профиль | Отправить 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
Благодарности: 8

Профиль | Отправить PM | Цитировать


Iska, благодарю, данный вид xml обрабатывает.
проблема в том, что я не владею powershell.
Могли бы Вы снабдить каждую строку комментарием ?
был бы весьма признателен, так как есть еще несколько типов xml документов и мне было бы совестно каждый раз просить о помощи
всего 5 видов документов и не регулярно меняется схема документа (на данный момент уже 8). вот именно из-за изменения схемы и потребовалась помощь..

Последний раз редактировалось firstarey, 16-05-2014 в 06:13.


Отправлено: 07:24, 06-03-2014 | #5


Ветеран


Сообщения: 27449
Благодарности: 8086

Профиль | Отправить 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», осуществляющему вывод в файл. ↑↑
Это, разумеется, не единственный способ. Возможно, удобнее будет сделать схему трансформации xsl и применять её к файлу xml.

Цитата firstarey:
всего 5 видов документов и не регулярно меняется схема документа (на данный момент уже 8). вот именно из-за изменения схемы и потребовалась помощь..
Вложения 05-01.zip »
Что, как надо извлечь, в каком виде надо представить результат.
Это сообщение посчитали полезным следующие участники:

Отправлено: 22:00, 06-03-2014 | #6


Старожил


Сообщения: 415
Благодарности: 257

Профиль | Отправить 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
Благодарности: 8

Профиль | Отправить PM | Цитировать


Iska, Искренне Вам благодарен. Комментарии в сопровождении иллюстраций весьма емко объясняют работу PowerShell с xml.
Пожалуй попробую переписать свою монстро-cmd на powerShell. если есть интерес могу выложить то, как это работало с примерами.

Anonymоus, благодарю что откликнулись, но у меня данный вариант не захотел обрабатывать xml (создает пустой файл csv и пишет All done). попробовал на нескольких xml.

как написал выше попробую переписать все под PowerShell - он значительно легче работает с xml

Отправлено: 07:11, 07-03-2014 | #8


Старожил


Сообщения: 415
Благодарности: 257

Профиль | Отправить PM | Цитировать


firstarey, на вашем xml из этого сообщения у меня (Win7 x64) всё отработало, я протестировал перед тем, как постить. В голову приходит разве что проблема с кириллицей в имени файла, если вы сохранили скрипт не в 866 кодировке.
UPD: запустил на виртуалке (XP SP3 x86) - действительно, не заработало. Моя ошибка, недостаточно тщательно протестировал. Исправил сообщение со скриптом.

Последний раз редактировалось Anonymоus, 07-03-2014 в 09:09.

Это сообщение посчитали полезным следующие участники:

Отправлено: 08:49, 07-03-2014 | #9


Пользователь


Сообщения: 102
Благодарности: 8

Профиль | Отправить 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"))
)
1. берем из указанной параметром %~1 строки первую подстроку разделенную < и >. как работает %%?
2. присвоить переменной data.. а что ей присвоить ?
3. проверку тоже не понял. как сработает проверка значения ? переменная без первых двух значений проверяется на равенство самой себе
4. set data тоже не понял..
5. опять не понял..


Боюсь Ваше решение моего вопроса принесло еще больше вопросов
на самом деле мне действительно интересно понять как работает Ваша конструкция и я надеюсь Вы прокомментируете более подробно описанные моменты.

Отправлено: 11:46, 07-03-2014 | #10



Компьютерный форум OSzone.net » Программирование, базы данных и автоматизация действий » Скриптовые языки администрирования Windows » CMD/BAT - [решено] парсинг xml :)

Участник сейчас на форуме Участник сейчас на форуме Участник вне форума Участник вне форума Автор темы Автор темы Шапка темы Сообщение прикреплено

Похожие темы
Название темы Автор Информация о форуме Ответов Последнее сообщение
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




 
Переход