Компьютерный форум OSzone.net  

Компьютерный форум OSzone.net (http://forum.oszone.net/index.php)
-   Скриптовые языки администрирования Windows (http://forum.oszone.net/forumdisplay.php?f=102)
-   -   [решено] парсинг xml :) (http://forum.oszone.net/showthread.php?t=278743)

firstarey 05-03-2014 09:46 2319305

парсинг xml :)
 
Суть:
Имеем 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

Iska 05-03-2014 09:51 2319309

Цитата:

Цитата firstarey
каким образом можно вытащить и связать в правильном соответствии номер точки и координаты, если они в разных строках »

Перейти к использованию правильных WSH/PoSH.

Цитата:

Цитата firstarey
можно ли сразу вытаскивать лишь значения (вместо x="515401.19" сразу 515401.19) чтобы иметь возможность записывать в csv »

Всё можно. Если Вы упакуете образец Вашего xml в архив, который приложите к сообщению, и точно укажете, что потребно извлечь и как именно записать.

firstarey 05-03-2014 12:26 2319397

Цитата:

Цитата 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>

Iska 05-03-2014 14:10 2319443

Попробуйте так (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


firstarey 06-03-2014 07:24 2319777

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

Iska 06-03-2014 22:00 2320063

Код:

# Путь к файлу 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 »

Что, как надо извлечь, в каком виде надо представить результат.

Anonymоus 07-03-2014 02:19 2320202

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

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

firstarey 07-03-2014 07:11 2320236

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

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

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

Anonymоus 07-03-2014 08:49 2320257

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

firstarey 07-03-2014 11:46 2320313

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. опять не понял..


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

Anonymоus 07-03-2014 12:52 2320343

Цитата:

Цитата firstarey
1. берем строку целиком
2. присваиваем в переменную data (почему в кавычках??)
3. если переменная block равна true если нашли строку начинающуюся на </Parcel (т.е. тут сначала выполняется второе if ? если выполнилось второе if, то переменной Block присвоить true?)
4. в переменную block записать значение false
5. вызываем blockData из цикла
6. пропускаем строку (так понимаю для наглядности отображения при выполнении, чтобы разбить блоки пустой строкой ?)
7. не понял эту строку.. в цикле берем первую подстроку после "=" из..(что делает ('Set BlockData_')??) и далее для меня тоже не понятно что выполняется в Do (Set %%B »

1. Берём строку целиком.
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 имя_переменной или часть имени выведет все переменные, с именем, подходящим под заданную строку в формате имя=значение), берем первую часть до = (имя) и удаляем их (назначаем пустое значение)
Всё, с основной частью закончили, осталось разобрать функции.

Цитата:

Цитата firstarey
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. повторили поиск нужной строки.. »

1. For %%A In (n d) Do Set %%A=0 - всего лишь вариант записи Set n=0 & Set d=0 Возможно, проще, правильнее и понятнее было бы записать именно так, но я по привычке засунул присвоение в цикл.
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.

Цитата:

Цитата firstarey
1. берем из указанной параметром %~1 строки первую подстроку разделенную < и >. как работает %%?
2. присвоить переменной data.. а что ей присвоить ?
3. проверку тоже не понял. как сработает проверка значения ? переменная без первых двух значений проверяется на равенство самой себе
4. set data тоже не понял..
5. опять не понял.. »

1. Тут всё совсем просто. В первом параметре для функции содержится строка, которую надо обработать. Очищаем её от двойных кавычек (с помощью ~) и в цикле убираем угловые скобки, они же операторы перенаправления - они могут нам помешать. В цикле - потому что это проще, чем дважды вызывать Set. %%? - то же самое, что %%[A-aZ-z], просто вместо буквенного значения имени переменной используется вопросительный знак, это допустимо, хотя и не указано в справке к For /?
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" "/"
5. В следующем цикле разбираем строку на подстроки, т.е.
Код:

Ordinate
X=334527.77
Y=2175865.20
Ord_Nmb=1
Geopoint_Zacrep=626003000000
Delta_Geopoint=0.10
/

и далее, используя ещё один цикл, разбиваем на ещё более мелкие подстроки, используя в качестве разделителя "=", после чего присваиваем всё в форме ключ=значение, т.е. получаем переменную Delta_Geopoint со значением 0.10 и так далее. Для чего такая сложная система разбора подстрок? Чтобы не бежать переписывать скрипт, если вдруг во входных данных поменяются местами параметры, например X с Y, или ещё как изменится структура документа.
Да, по сути это куча велосипедных реализаций и говнокода, но, к сожалению, cmd не имеет нативных инструментов для работы со структурированными данными вроде XML. Реализация на PowerShell, которую написал товарищ Iska, гораздо быстрее и проще, но и cmd пока рано списывать со счетов.


Время: 21:05.

Время: 21:05.
© OSzone.net 2001-