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

Компьютерный форум OSzone.net (http://forum.oszone.net/index.php)
-   AutoIt (http://forum.oszone.net/forumdisplay.php?f=103)
-   -   [решено] Поиск <строки> в XML (http://forum.oszone.net/showthread.php?t=190472)

semiono 07-11-2010 01:04 1536588

Поиск <строки> в XML
 
В файле надо найти строку, например:
<WIDTH int="800" />
и заменить её на
<WIDTH int="1024" />
Но лучше найти с неизвестным значением.
Я думаю здесь можно пропарсить по открытию тега "<" плюс имя "WIDTH int="
и собственно по закрытию /> И перезаписать найденный шаблон строки.

Нечто такое, только без ошибок можно?
Код:

$file = FileOpen("C:\I\WinSxS\Process\Application Data\codeblocks\default.conf",0)
$i = 0
While $i <> 1
        FileSetPos($file,$i,0)
        $i = $i + 1
        $str = FileRead($file,1)
        If StringCompare($str,"<",0) then MsgBox(0,"",$str,"","")
WEnd
FileClose($file)

я тут начал побайтно искать "<" только в качестве примера... искать надо конечно же <WIDTH int="
Файл собой представляет типичный XML

Здесь мы уже решали однажды проблему, но там обошлось хитростью с IniWright. Но тут это врядли поможет.
Кстати, может кто знает что за файл лежит system32\xmlparser.exe
Может этим как-то можно пользоваться для такой задачи?


Самое главное чуть не забыл, я именно хотел организовать побайтное сравнение, чтобы найти начало строки.
И потом если начало найдено, то найти и сравнить строку итд...
Иначе если с помощью ReadFile читать, то можно наткнуться на пробелы и отступы итп.
Вообщем я думаю надо искть "<" и ">", а потом дополнительно парсить то что внутри.
Скорость работы скрипта вообщем-то не важно. Помогите такой скрипт зделать? Или какие идеи есть ещё.
:)

amel27 07-11-2010 03:24 1536620

Цитата:

Цитата semiono
В файле надо найти строку, например:
<WIDTH int="800" />
и заменить её на
<WIDTH int="1024" />
Но лучше найти с неизвестным значением. »

Код:

$sFile = "C:\I\WinSxS\Process\Application Data\codeblocks\default.conf"
$sText = FileRead($sFile)
$sText = StringRegExpReplace($sText, '(?i)<WIDTH\s+int="\d+"\s*/>', '<WIDTH int="1024" />')
If @extended Then
        $hFile =FileOpen($sFile,2)
        FileWrite($hFile,$sText)
        FileClose($hFile)
EndIf


semiono 07-11-2010 04:34 1536632

Спасибо большое, очень полезный скрипт! Некоторые программы не используют реестр, так к ним не подобраться без этого было! :)
А можно как-то многостроковые блоки выхватывать из XML, так как я боюсь что такое мне тоже наверняка встретится. Я имею ввиду где начало "<" и завершение "/>" внутри содержат @CRLF строчки? Искать по контенту, например, найти:
"<FFFF" конец будет само собой первый попавшийся тэг "/>"

Iska 07-11-2010 04:43 1536634

Если файл действительно содержит xml, то и работать с ним можно и нужно теми же средствами: Сценарии объектной модели документа DOM XML средствами OLE-Automation.

amel27 07-11-2010 05:05 1536635

Цитата:

Цитата semiono
А можно как-то многостроковые блоки выхватывать из XML »

можно, но нужно хорошо представлять себе структуру документа, "универсального" варианта тут нет
...либо используйте DOM, как подсказал Iska ... :)
Цитата:

Цитата semiono
"<FFFF" конец будет само собой первый попавшийся тэг "/>" »

теги могут быть вложенными


Iska, увы, далеко не всегда доступна полная инфа о структуре документа - чаще есть только несколько примеров, на основе которых приходится строить свои предположения о возможной структуре

semiono 07-11-2010 05:11 1536637

Код:

$sFile = @AppDataDir & "\codeblocks\default.conf"
$sText = FileRead($sFile)
$sText = StringRegExpReplace($sText, '(?i)<WIDTH\s+int="\d+"\s*/>', '<WIDTH int="'& @DesktopWidth &'" />')
If @extended Then
        $hFile =FileOpen($sFile,2)
        FileWrite($hFile,$sText)
        FileClose($hFile)
EndIf

Хорошее начало! :)

Цитата:

Цитата amel27
теги могут быть вложенными »

Я всегда анализирую конфиг и тестирую потом, хотя ошибки всегда могут быть. :)

Цитата:

Цитата amel27
либо используйте DOM, как подсказал Iska »

Я не понял, там об инструменте ни слова не сказанно? Или я не понял, чем загружать чем править?
Это скорее для html аддонов инструкции, причём которые пишут, на м же надо их удалённо-пакетно править налету :)
?

ЗЫ Идея налету родилась! А что если вывести StringRegExpReplace() в STDINPUT консоли, зделать типа утилиту! )))
Я понимаю есть всякие PERL итп. но чисто поэкспереминтировать? Или это сложный и бажистый скрипт получиться?
Я имею ввиду зделать (AutoIt) StringRegExpReplace.exe --help :)

amel27 07-11-2010 05:20 1536639

Цитата:

Цитата semiono
там об инструменте ни слова не сказанно »

обычный VBS-скрипт, который при желании можно переделать на AutoIT

semiono 07-11-2010 05:30 1536641

amel27, при возможности напиши пример "<SOMETHING" & @CRLF & "/>",
а я потом подумаю как применять, в любом случае пригодиться такое. Но я сам тоже подумаю может и сам справлюсь. Про vbs спасиб! Теперь ясно!

Кстати, говоря vbs могут не работать из-за всяких твиков системы, а autoit даже от себя не заввисим! :)

amel27 07-11-2010 12:15 1536769

Цитата:

Цитата semiono
напиши пример "<SOMETHING" & @CRLF & "/>"

и что с этим делать?.. в первом посте было понятно что и на что менять

Creat0R 07-11-2010 13:10 1536812

Для работы с xml есть библиотека: XML DOM wrapper (COM).

semiono 07-11-2010 20:00 1537070

Цитата:

Цитата amel27
и что с этим делать?.. в первом посте было понятно что и на что менять »

Сейчас попытаюсь реальный пример показать, может быть не самый удачный, но навскидку...

Код:

                        <gcc>
                                <NAME>
                                        <str>
                                                <![CDATA[GNU GCC Compiler]]>
                                        </str>
                                </NAME>
                                <MASTER_PATH>
                                        <str>
                                                <![CDATA[C:\Program Files\mingw32]]>
                                        </str>
                                </MASTER_PATH>
                        </gcc>
                        <msvctk>
                                <NAME>
                                        <str>
                                                <![CDATA[Microsoft Visual C++ Toolkit 2003]]>
                                        </str>
                                </NAME>
                                <INCLUDE_DIRS>
                                        <str>
                                                <![CDATA[C:\Program Files\Microsoft Visual C++ Toolkit 2003\include;]]>
                                        </str>
                                </INCLUDE_DIRS>
                                <LIBRARY_DIRS>
                                        <str>
                                                <![CDATA[C:\Program Files\Microsoft Visual C++ Toolkit 2003\lib;]]>
                                        </str>
                                </LIBRARY_DIRS>
                                <MASTER_PATH>
                                        <str>
                                                <![CDATA[C:\Program Files\Microsoft Visual C++ Toolkit 2003]]>
                                        </str>
                                </MASTER_PATH>
                        </msvctk>

Вот тут видно что два блока настроек: <gcc> < /gcc> и <msvctk>< /msvctk>, и хотелось бы пробраться к MASTER_PATH первого кампилятора, то-есть иметь возможность указать путь <![CDATA[C:\Program Files\mingw32]]> допустим на D:\Other\mingw32

Здесь можно лишь сложить все теги <gcc> + <NAME> + <str> + <![CDATA[GNU GCC Compiler]]> ...
+ <MASTER_PATH> + <str> + <![CDATA[C:\Program Files\mingw32]]>
игнорируя табуляцию и пробелы... либо удалив их либо установив свою произвольную табуляцию, чтобы отредактировать этот блок автоитом. Наверное это сложная задача, но может от того и интереснее. И тогда уже не будет непроходимых XML-ов!
:)

Creat0R 08-11-2010 00:26 1537290

Вот так можно получить данные:

Код:

#include <_XMLDomWrapper.au3>

$FILE = @ScriptDir & "\test.xml"
$READ = FileRead($FILE)
$iLoad = _XMLLoadXML($READ)
If Not $iLoad Then Exit -1

$sPath = _XMLGetValue("/file/gcc/MASTER_PATH/str")
ConsoleWrite($sPath[1] & @LF)

записать обратно с этой библиотекой мне пока не удалось, поэтому вот пример с парсированием строк:

Код:

_XMLSetValue("Test.xml", "gcc/MASTER_PATH/str", "D:\Other\mingw32")

Func _XMLSetValue($sFile, $sPath, $sValue)
    $sRead = FileRead($sFile)
    $sPattern = ''
    $sData = ''

    $aSplitPath = StringSplit($sPath, '/')

    For $i = 1 To $aSplitPath[0]
        $sPattern &= '.*?<' & $aSplitPath[$i] & '>.*?'
    Next

    $sData = StringRegExpReplace($sRead, '(?si)(' & $sPattern & '\Q<![CDATA[\E).*?(\Q]]>\E).*?', '\1' & StringReplace($sValue, '\', '\\') & '\2')

    $hFile = FileOpen($sFile, 2)
    FileWrite($hFile, $sData)
    FileClose($hFile)
EndFunc


amel27 08-11-2010 05:43 1537352

semiono, как-то так:
Код:

$sFile = "1.xml"
$sText = FileRead($sFile)
$sRExp = '(?ims)('& _
        '(?>^\s*+<gcc>).*?'& _
        '(?>^\s*+<MASTER_PATH>).*?'& _
        '(?>^\s*+<str>).*?'& _
        '(?>^\s*+<!\[CDATA\[))' & '([^]]*+)' & '(.*?'& _
        '(?>^\s*+</gcc>)'& ')'
$sText = StringRegExpReplace($sText, $sRExp, "$1D:\\Other\\mingw32$3")
If @extended Then
        $hFile =FileOpen($sFile,2)
        FileWrite($hFile,$sText)
        FileClose($hFile)
EndIf

всё-таки для работы с DOM нужен полный файл, а не его кусок

semiono 11-11-2010 16:29 1540167

amel27, невозможно использовать переменные, sRExp удаляет слеши в пути:

$sText = StringRegExpReplace($sText, $sRExp, "$1"& @ProgramFilesDir &"\\mingw32$3")
А без переменных ничего хорошего не напишешь, надо что-то придумать тут!
Я в паттернах не шарю, сам не смогу поправить.
Кстати, не очень понятно зачем надо \ фильтровать, ведь в шаблоне файла только прямые / слеши?
Или это фитча в StringRegExpReplace()

Creat0R 11-11-2010 18:43 1540292

Цитата:

Цитата semiono
надо что-то придумать тут! »

То что “я придумал” в своём сообщений не подходит?

Код:

$sText = StringRegExpReplace($sText, $sRExp, "$1"& StringReplace(@ProgramFilesDir, "\", "\\") &"\\mingw32$3")

semiono 11-11-2010 19:21 1540315

Цитата:

Цитата Creat0R
записать обратно с этой библиотекой мне пока не удалось, »

Я не стал доделывать то что не удалось, уж мне-то тем более не удалось бы.
А вот сейчас хороший вариант! :)

Ой, стоп! Я не понял, я думал это не рабочий пример...
Цитата:

Цитата Creat0R
поэтому вот пример с парсированием строк: »

Блин, я не сообразил. Я тугодум :)

semiono 11-11-2010 19:43 1540328

Я извиняюсь, вот ещё один пример реальный надо доделать. И тему можно закрыть.
Код:

                        <bcc>
                                <NAME>
                                        <str>
                                                <![CDATA[Borland C++ Compiler (5.5, 5.82)]]>
                                        </str>
                                </NAME>
                                <INCLUDE_DIRS>
                                        <str>
                                                <![CDATA[C:\I\Apps\Borland\CBuilder5\include;]]>
                                        </str>
                                </INCLUDE_DIRS>
                                <LIBRARY_DIRS>
                                        <str>
                                                <![CDATA[C:\I\Apps\Borland\CBuilder5\lib;C:\I\Apps\Borland\CBuilder5\lib\psdk;]]>
                                        </str>
                                </LIBRARY_DIRS>
                                <MASTER_PATH>
                                        <str>
                                                <![CDATA[C:\I\Apps\Borland\CBuilder5]]>
                                        </str>
                                </MASTER_PATH>
                        </bcc>

Я боюсь испортить скрипт, тут в блоке сразу аж три строки.
Я бы сам тупо зделал три прохождения одного блока, но наверное есть способ лучше?

Должно быть так:
1. @SystemDir & "\include"
2. @SystemDir & "\lib;" & @SystemDir & "\lib\psdk"
3. @SystemDir


И спасибо ещё раз!!!

Creat0R 11-11-2010 19:51 1540333

Цитата:

Цитата semiono
Я боюсь испортить скрипт »

А что там портить, используй функцию по назначению:

Код:

_XMLSetValue("Test.xml", "bcc/INCLUDE_DIRS/str", @SystemDir & "\include")
_XMLSetValue("Test.xml", "bcc/LIBRARY_DIRS/str", @SystemDir & "\lib;" & @SystemDir & "\lib\psdk")
_XMLSetValue("Test.xml", "bcc/MASTER_PATH/str", @SystemDir)


semiono 16-11-2010 15:57 1544021

Пример.
Код:

<!-- ArrangeWindow -->
      <item>
        <string name="Group" value="PArrangeWindow"/>
        <member name="Values">
            <int name="ActiveContext Only" value="0"/>
            <int name="Auto Scroll OnOff" value="1"/>
            <int name="Display Format" value="0"/>
            <float name="Insert Time" value="0"/>
            <obj class="UPoint" name="Location" ID="182867016">
              <int name="H" value="0"/>
              <int name="V" value="0"/>
            </obj>
            <int name="Manipulate Suspends AutoScroll" value="1"/>
            <int name="Multi TrackType" value="1"/>
            <int name="PPQ Linear" value="0"/>
            <int name="Scale Mode Film" value="3"/>
            <int name="Scale Mode Frames" value="3"/>
            <int name="Scale Mode PPQ" value="0"/>
            <int name="Scale Mode Samples" value="3"/>
            <int name="Scale Mode Seconds" value="3"/>
            <int name="Show Info" value="0"/>
            <int name="Show Inspector" value="1"/>
            <int name="Show Overview" value="0"/>
            <int name="Show Track Colors" value="1"/>
            <obj class="UPoint" name="Size" ID="182564128">
              <int name="H" value="1132"/>
              <int name="V" value="732"/>
            </obj>
            <int name="Snap Mode" value="0"/>
            <int name="Snap State" value="1"/>
            <int name="Speaker Arr" value="1"/>
            <member name="Tool Configuration">
              <int name="Select Tool" value="0"/>
              <int name="Range Tool" value="0"/>
              <int name="Split Tool" value="-1"/>
              <int name="Glue Tool" value="-1"/>
              <int name="Delete Tool" value="-1"/>
              <int name="Zoom Tool" value="-1"/>
              <int name="Mute Tool" value="-1"/>
              <int name="TimeWarp Tool" value="0"/>
              <int name="Draw Tool" value="-1"/>
              <int name="Curve Tool" value="0"/>
              <int name="Play Tool" value="0"/>
              <int name="Color Tool" value="-1"/>
              <int name="SelectedToolIndex" value="0"/>
            </member>
            <float name="Window Zoom Factor" value="0.021212121212121212"/>
        </member>
      </item>

Здесь нужно установить значения для "H" и для "V"

<int name="H" value="1024"/>
<int name="V" value="768"/>

Значения должны быть таковы - @DesktopWidth, @DesktopHeigh.
Просмотрел весь файл, практически схема повторяется одна и та же.

<int name="..." value="..."/>
<float name="..." value="..."/> итд.

Все заголовки вида <!-- ArrangeWindow --> , кажись это коментарий так пишется в XML.

Мне очень нужно скрипт под этот XML. :(

Creat0R, я не уверен пойдёт ли то, что ты написал без изменений?
Func _XMLSetValue($sFile, $sPath, $sValue)
...

Мне тяжело расшифровать StringRegExpReplace() паттерн, может там даже лишнее или не хватает чего?
Помоги под эту задачу, покажи как должно быть?

Кстати, скачал _XMLDomWrapper.au3 долго глядел в него, но без справки вообще ловить нечего.
Возможно там _XMLUpdateField как-то работает с полями XML, но не ясно, что именно делать :)

semiono 16-11-2010 16:30 1544044

Я тут смотрю, в качестве заголовка идёт такое
<obj class="UPoint" name="Size" ID="182564128">
Однако ID="182564128" может изменяться, поэтому его значение надо игнрировать.

Вобщем нужен шаблон чтобы привести к такому виду грубо говоря

<!-- ArrangeWindow --> (это тоже должно быть, ато там и другие секции есть)
...
начало: <obj class="UPoint" name="Size" ID="????">
<int name="H" value="@DesktopHeight"/>
<int name="V" value="@DesktopWidth"/>
конец: </obj>


По-моему код может быть таким -
Рид файл
компаре стринг
иф <!-- ArrangeWindow --> = 1
тогда
компаре стринг
итд.
сравниваем строки и двигаемся вперёд. ??

:)

Creat0R 16-11-2010 23:11 1544352

Цитата:

Цитата semiono
пойдёт ли то, что ты написал без изменений? »

Нет, это уже специфичная задача.

Код:

$sFile = @DesktopDir & "\Test.xml"
$sHValue = @DesktopWidth
$sVValue = @DesktopHeight

$sData = FileRead($sFile)
$sData = StringRegExpReplace($sData, '(?si)(<obj.*name="Location".*?>.*?(?!</obj>)<int name="H" value=")\d*("/>)', '${1}' & $sHValue & '$2')
$sData = StringRegExpReplace($sData, '(?si)(<obj.*name="Location".*?>.*?(?!</obj>)<int name="V" value=")\d*("/>)', '${1}' & $sVValue & '$2')

$hFile = FileOpen($sFile, 2)
FileWrite($hFile, $sData)
FileClose($hFile)


semiono 16-11-2010 23:53 1544386

Большое спасибо!
А глобальный заголовок проверить?

<!-- ArrangeWindow -->
....

<obj class="UPoint" name="Size" ID="182564128">
<int name="H" value="1152"/>
<int name="V" value="864"/>
</obj>
...

<!-- Event Image -->
...

<!-- Enable Record on Select Track -->
....

<!-- Registration -->
...итд

Без этого не получится к сожалению. Может через If EndIf как-то?

Цитата:

Цитата Creat0R
Нет, это уже специфичная задача. »

Я бы сказал даже отвратительно запутанная.

semiono 17-11-2010 00:18 1544402

$sData = FileRead($sFile)
If StringInStr($sData, "<!-- ArrangeWindow -->") <> 0 Then
$sData = StringRegExpReplace(...

Кажись работает! Только наверное не совсем правильно?

Creat0R 17-11-2010 00:32 1544412

Цитата:

Цитата semiono
наверное не совсем правильно? »

Почему нет, если оно должно присутствовать в файле, значит видимо проверка не лишняя.

semiono 17-11-2010 01:00 1544437

Нет, не работает.

Если две секции

<!-- ArrangeWindow -->
...
<!-- Event Image -->
...

то StringRegExpReplace() работает всегда с последней секцией.
StringInStr просто проверяет весь файл на наличие <!-- ArrangeWindow -->
А вот как зделать чтобы обрабатывался именно блок <!-- ArrangeWindow -->

http://forum.oszone.net/post-1544021-19.html

Зацепка только одна, то что в конце всегда стоит безымянный тэг </item>

Creat0R 17-11-2010 15:12 1544842

Цитата:

Цитата semiono
tringRegExpReplace() работает всегда с последней секцией »

Он работает с той где найдено совпадение по шаблону. У тебя постоянно появляются новвые условия задачи, нужно либо учить RegExp, либо чётко знать что требуется получить.

Код:

$sData = StringRegExpReplace($sData, '(?si)(<!-- ArrangeWindow -->.*?<obj.*name="Location".*?>.*?(?!</obj>)<int name="H" value=")\d*("/>)', '${1}' & $sHValue & '$2')

semiono 17-11-2010 17:13 1544994

Creat0R, спасибо! Мне этого скрипта хватит, просто надо было доделать, очень нужно было позарез.
XML очень непроходимый, поэтому я условия не сразу учёл, и даже удивился, два текста встретились,
и оба совершенно различной структуры! При такой любви производителей к xml, и неудосужились внедрить
winapi для работы с этим в винду. :search:

Creat0R 17-11-2010 18:48 1545079

semiono,
Тема решена?

semiono 17-11-2010 19:53 1545134

Тема решена. :(
Мне стыдно, я вдруг вспомнил это я сам коментарии писал давно очень и забыл:
<!-- ArrangeWindow -->
<!-- Event Image --> итп. это действительно были коменты, причём от меня ))

Структура файла:
...
<item>
<string name="Group" value="PArrangeWindow"/>
...
<obj class="UPoint" name="Location" ID="182867016">
<int name="H" value="0"/>
<int name="V" value="0"/>
</obj>
...
<obj class="UPoint" name="Size" ID="182564128">
<int name="H" value="1132"/>
<int name="V" value="732"/>
</obj>
...
</item>
...
Но уже тему закрою, сам виноват, надо было вовремя разбираться.


Время: 14:55.

Время: 14:55.
© OSzone.net 2001-