Что нового

[Массивы] Как правильно вытащить данные из XML файла

CMAUJI

Новичок
Сообщения
2
Репутация
0
Добрый день.
Есть XML файл и из него необходимо вывести, в виде списка, все значений "DisplayName".

XML файл очень большой, выкладываю урезанный вариант.

Код:
<?xml version="1.0"?>
<UserGroupList>
	<UserGroupItem GUID="{7B86B856-1DF1-43B0-B90A-641E11E80C3F}" DisplayName="ad"/>
	<UserGroupItem GUID="{C744D873-1FD0-49E5-B873-B794CFB959F5}" DisplayName="H2"/>
	<UserGroupItem GUID="{FC62FF9C-89BD-4FA9-9AAC-8E035174CEDD}" DisplayName="test"/>
</UserGroupList>

Пробовал делать через массив, но что-то не получилось, пример:

Код:
#include <_XMLDomWrapper.au3>
#include <Array.au3>

$GroupXML = _XMLFileOpen("D:\group.xml")

Dim $aAttName[1], $aAttVal[1], $ret
For $i = 1 to $i+1
	$ret = _XMLGetAttrib("/UserGroupList/*["&$i+1&"]", $aAttName, $aAttVal)
_ArrayDisplay($aAttName,"UserGroupItem")
_ArrayDisplay($aAttVal,"UserGroupItem")

	$node = _XMLGetAttrib("/UserGroupList/*["&$i+1&"]","DisplayName",'[@DisplayName='&$i&']')
MsgBox(0,"Name Where Id="&$i,$node)
if @error then ExitLoop
Next
Exit


Подскажите, пожалуйста, где ошибся.
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,665
Репутация
2,463
Тут одинаковые пункты, я не думаю что это правильная запись xml, в таком случае я бы использовал RegExp:

Код:
$vTest = FileRead(@ScriptDir & "\group.xml")
$aRet = StringRegExp($vTest, '<UserGroupItem GUID=".*" DisplayName="(.*)"/>', 3)

_ArrayDisplay($aRet)


А вообще, если бы это был правильный xml, то можно было бы получать значения так:

Код:
#include <_XMLDomWrapper.au3>
#include <Array.au3>

$GroupXML = _XMLFileOpen(@ScriptDir & "\group.xml")

$sRoot = "UserGroupList"
$aChilds = _XMLGetChildText($sRoot)
;~ _ArrayDisplay($aChilds)

Dim $aName[1], $aValue[1]

For $i = 1 To UBound($aChilds)-1
	$aAttribs = _XMLGetAllAttrib($sRoot & "/" & $aChilds[$i], $aName, $aValue)
	_ArrayDisplay($aAttribs)
Next
 

kaster

Мой Аватар, он лучший самый
Команда форума
Глобальный модератор
Сообщения
4,020
Репутация
622

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,665
Репутация
2,463
Kaster [?]
там нет одинаковых пунктов
Ну как нет, в файле мы имеем 3 одинаковых пути «UserGroupList/UserGroupItem».
Как ты предлагаешь из этого вытащить значение/аттрибуты?
У меня не получилось.
 

kaster

Мой Аватар, он лучший самый
Команда форума
Глобальный модератор
Сообщения
4,020
Репутация
622
CreatoR
атрибутами являются GUID и DisplayName, которые имеют разные значения для каждого поля. все по формату. а по поводу как вытаскивать, честно говоря я не знаком с библиотекой XMLDomWrapper, и не в курсе, как он вытаскивает эти значения, поэтому, к сожалению, не могу ничего подсказать :(
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,665
Репутация
2,463
Kaster [?]
атрибутами являются GUID и DisplayName, которые имеют разные значения для каждого поля. все по формату
Какому формату?

Ну представь себе, ты хочешь прочитать данные из ini-файла, а там такое содержимое:

INI:
[Section]
Key1=Some Value
Key1=Other Value

Стандартные Ini* функций с этим не справляются, потому что это неправильная запись, ключ должен быть уникальным.
Так и
тут, Item'ы одинаковые, прочитать стандартным способом (в данном случае используя спец. библиотеку) не получится.

Странно, IniReadSection читает оба ключа.
 

kaster

Мой Аватар, он лучший самый
Команда форума
Глобальный модератор
Сообщения
4,020
Репутация
622
CreatoR
ini и xml отличаются. формат можно глянуть в вики. там конечно не все, но основное есть.
Синтаксис XML
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,665
Репутация
2,463
Вот, так можно оказывается:

Код:
#include <_XMLDomWrapper.au3>
#include <Array.au3>

$GroupXML = _XMLFileOpen(@ScriptDir & "\group.xml")

$sRoot = "UserGroupList"
$aChilds = _XMLGetChildText($sRoot)

Dim $aName[1], $aValue[1]

For $i = 1 To UBound($aChilds)-1
	$aAttribs = _XMLGetAllAttribIndex($sRoot & "/" & $aChilds[$i], $aName, $aValue, "", $i-1)
	_ArrayDisplay($aAttribs)
Next



Добавлено:
Сообщение автоматически объединено:

Kaster [?]
ini и xml отличаются
Ты серъёзно думаешь что я не знаю этого? :scratch:

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

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,320
Господа, дайте, пожалуйста, ссылку на _XMLDomWrapper.au3.
 

kaster

Мой Аватар, он лучший самый
Команда форума
Глобальный модератор
Сообщения
4,020
Репутация
622
CreatoR
тогда мне не понятно, зачем ты приводишь в пример ini. и мне не понятно, что ты подразумеваешь под слово "путь". в xml нет никаких путей. есть корневой элемент, есть родительские узлы и дочерние, которые имеют атрибуты и значения. приведенный в шапке файл является нормальным, с точки зрения формата, xml файлом
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,665
Репутация
2,463
Kaster [?]
тогда мне не понятно, зачем ты приводишь в пример ini
Это был не совсем правильный пример, но суть была в том что не должны быть одинаковые ключи.

мне не понятно, что ты подразумеваешь под слово "путь". в xml нет никаких путей.
Как нет, в первом сообщений содержимое xml имеет путь «UserGroupList/UserGroupItem».

есть корневой элемент, есть родительские узлы и дочерние
Это и называется путь :smile:


Добавлено:
Сообщение автоматически объединено:

madmasles [?]
дайте, пожалуйста, ссылку на _XMLDomWrapper.au3.
Вот ещё тема на оф. форуме: XML DOM wrapper (COM)
 

Yuri

AutoIT Гуру
Сообщения
737
Репутация
282
Еще вот такое решение:
Код:
$file = FileOpen("group.xml", 0) ;открыть файл XML для чтения, который находится в одной директории со скриптом
If $file = -1 Then ;если не удалось открыть, тогда 
    MsgBox(16, "Ошибка", "Не удалось открыть файл.") ;сообщить об ошибке
    Exit ;и выйти
EndIf

While 1 ;в цикле
    $line = FileReadLine($file) ;читаем файл XML по-строчно в переменную $line
    If @error = -1 Then ExitLoop ;при достижении конца файла - выходим из цикла   
	If StringInStr($line, "DisplayName=") <> 0 Then	;если в текущую прочитанную строку входит подстрока DisplayName=, тогда
		$array = StringSplit($line, '"') ; разбиваем эту текущую строку на массив подстрок, разделяя кавычками
		If UBound($array) >= 6 Then	; а если в массиве от 6 и более индексов			
			ConsoleWrite($array[4] & @CRLF) ; тогда вывести в список консоли 4-й индекс.
		EndIf
	EndIf
Wend
FileClose($file) ;закрыть ранее открытый файл XML
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,665
Репутация
2,463
Yuriy [?]
Еще вот такое решение
С первого сообщения понятно, что требуется это делать посредством библиотеки «_XMLDomWrapper.au3».
В любом случае, если уже делать не через эту библиотеку, то надёжнее через RegExp, как я и показал выше.
 

dwerf

Использует ArchLinux
Сообщения
478
Репутация
219
Через MsXML, без _XMLDomWrapper.au3 (без лишних циклов).
Код:
#include <array.au3>

$oXml = ObjCreate('MSXML2.DomDocument.3.0')
If Not IsObj($oXml) Then
	MsgBox(16, 'Ошибка', 'MsXML 3.0 не установлен.')
	Exit 1
EndIf

$oXml.async = 0

$oXml.load('D:\group.xml')
;~ $oXml.loadXML('<?xml version="1.0"?><UserGroupList><UserGroupItem GUID="{7B86B856-1DF1-43B0-B90A-641E11E80C3F}" DisplayName="ad"/><UserGroupItem GUID="{C744D873-1FD0-49E5-B873-B794CFB959F5}" DisplayName="H2"/><UserGroupItem GUID="{FC62FF9C-89BD-4FA9-9AAC-8E035174CEDD}" DisplayName="test"/></UserGroupList>')

With $oXml.parseError
	If .errorCode <> 0 Then
		MsgBox(16, 'Ошибка', 'Код ошибки: ' & .errorCode & @CRLF & _
			'Номер строки с ошибкой: ' & .line & @CRLF & _
			'Позиция ошибки в строке: ' & .linePos & @CRLF & _
			'Позиция ошибки в файле: ' & .filePos & @CRLF & _
			'Причина ошибки: ' & .reason)
		Exit 2
	EndIf
EndWith

$oNodes = $oXml.selectNodes('/UserGroupList/UserGroupItem')
If $oNodes.length <= 0 Then
	MsgBox(16, 'Ошибка', 'Список пуст.')
	Exit 3
EndIf

;for-in
;~ For $oNode In $oNodes
;~ 	ConsoleWrite($oNode.xml & @LF)
;~ Next

;for-to
Dim $aDisplayNames[$oNodes.length]
For $i = 0 To $oNodes.length-1 Step +1
	$aDisplayNames[$i] = $oNodes.item($i).getAttribute('DisplayName')
Next

_ArrayDisplay($aDisplayNames)
 

ynbIpb

Скриптер
Сообщения
399
Репутация
110
Помогите пожалуйста прочитать XML с помошь _XMLDomWrapper
Код:
<?xml version="1.0" encoding="utf-8"?>
<XnaContent>
  <Texts>
    <Text id="TXT_PRESS_START">
      <en value="Press START" />
      <fr value="Appuyez sur START" />
    </Text>
    <Text id="TXT_ANY_KEY">
      <en value="Press anything to begin..." />
      <fr value="Appuyez sur une touche pour continuer..." />
    </Text>
    <Text id="TXT_LOADING">
      <en value="Loading... please wait!" />
      <fr value="Chargement en cours...Patientez !" />
    </Text>
    <Text id="TXT_NOT_IN_TRIAL">
      <en value="Not available in trial!" />
      <fr value="Dispo. dans le jeu complet" />
    </Text>
    <Text id="TXT_FOUND_BTM">
      <en value="You found a new Block That Matters!" />
      <fr value="Vous avez trouvé un nouveau Block That Matters !" />
    </Text>
    <Text id="TXT_PRESS_ANYTHING">
      <en value="Press anything to continue" />
      <fr value="Appuyez sur une touche pour continuer" />
    </Text>
    <Text id="TXT_ONE_HUNDRED">
      <en value="* Yayz! Get your cheap reward on swingswingsubmarine.com/BTM100 pswd: " />
      <fr value="* Bravo ! Une surprise vous attend sur swingswingsubmarine.com/BTM100 pswd: " />
    </Text>
    <Text id="TXT_ONE_HUNDRED_CHEATED">
      <en value="* You cheated to complete the game... no reward for this :], reset your game save!" />
      <fr value="* Quoi, vous avez triché pour finir le jeu ? Pas de cadeau alors :], effacez votre sauvegarde !" />
    </Text>
и т.д.
Нужно получить: Text id=, en value=, fr value=
 

Zaramot

I ♥ AutoIt
Сообщения
1,160
Репутация
660
Так сойдёт:
Код:
#include <Array.au3>

$XML = FileOpenDialog('Choose XML file', '', 'XML File (*.xml)')
If @error Then Exit
$Read = FileRead($XML)
$RegExp = StringRegExp($Read, '<.*="(.*?)"', 3)

_ArrayDisplay($RegExp)
 

ynbIpb

Скриптер
Сообщения
399
Репутация
110
Вот это я понимаю оптимизированный код :shok:
Не то, что мой:
Код:
$sTexts_xml_Path = FileOpenDialog ( "Выберите XML файл в формате UTF-8 (BOM)", @ScriptDir, "XML (*.xml)|Все (*.*)", 1)
If $sTexts_xml_Path = "" Then Exit
$hTexts_xml = FileOpen ($sTexts_xml_Path, 0+16) ; сначала открываем файл в бинарном режиме для чтения сигнатуры
$bBOM = FileRead ($hTexts_xml, 3)
FileClose ($hTexts_xml)
If $bBOM <> "0xEFBBBF" Then ; если сигнатура не равна BOM, возвращаемся
	MsgBox (0, "", "Файл не UTF-8 (BOM)")
	Exit
EndIf
$hTexts_xml = FileOpen ($sTexts_xml_Path, 0+128) ; открываем для чтения в UTF-8
$sTexts_xml_Data = FileRead ($hTexts_xml)
FileClose ($hTexts_xml)
$aTexts_xml_Data = StringSplit (StringStripCR ($sTexts_xml_Data), @LF); разбиваем на строки
$sResult = ""
For $i=1 To $aTexts_xml_Data[0]
	$iPos = StringInStr ($aTexts_xml_Data[$i], '<Text id="')
	If $iPos > 0 Then
		$aTexts_xml_Data[$i] = StringTrimLeft ($aTexts_xml_Data[$i], $iPos + 9)
		$iQts = StringInStr ($aTexts_xml_Data[$i], '"', 0, -1); определяем позицию кавычки с конца
		$sResult &= "[" & StringMid ($aTexts_xml_Data[$i], 1, $iQts-1) & "]" & @CRLF
		$iPosUn = StringInStr ($aTexts_xml_Data[$i+1], '<univ value="')
		If $iPosUn > 0 Then
			$aTexts_xml_Data[$i+1] = StringTrimLeft ($aTexts_xml_Data[$i+1], $iPosUn + 12)
			$iQts = StringInStr ($aTexts_xml_Data[$i+1], '"', 0, -1); определяем позицию кавычки с конца
			$sResult &= "univ=" & StringMid ($aTexts_xml_Data[$i+1], 1, $iQts-1) & @CRLF
		EndIf
		$iPosEn = StringInStr ($aTexts_xml_Data[$i+1], '<en value="')
		If $iPosEn > 0 Then
			$aTexts_xml_Data[$i+1] = StringTrimLeft ($aTexts_xml_Data[$i+1], $iPosEn + 10)
			$iQts = StringInStr ($aTexts_xml_Data[$i+1], '"', 0, -1); определяем позицию кавычки с конца
			$sResult &= "en=" & StringMid ($aTexts_xml_Data[$i+1], 1, $iQts-1) & @CRLF
		EndIf
		$iPosFr = StringInStr ($aTexts_xml_Data[$i+2], '<fr value="')
		If $iPosFr > 0 Then
			$aTexts_xml_Data[$i+2] = StringTrimLeft ($aTexts_xml_Data[$i+2], $iPosEn + 10)
			$iQts = StringInStr ($aTexts_xml_Data[$i+2], '"', 0, -1); определяем позицию кавычки с конца
			$sResult &= "fr=" & StringMid ($aTexts_xml_Data[$i+2], 1, $iQts-1) & @CRLF
		EndIf
	EndIf
Next
$hTexts_xml = FileOpen ($sTexts_xml_Path & ".txt", 2) 
FileWrite ($hTexts_xml, $sResult)
FileClose ($hTexts_xml)
MsgBox (0, "", "Done!")

Но хочется именно средствами _XMLDomWrapper, так как мне ведь ещё и обратно эти данные заносить придётся.
А так спасибо за вариант.
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,320
ynbIpb [?]
Но хочется именно средствами _XMLDomWrapper
Я с ними плохо знаком. :(
Вариант с RegExp:
Код:
Dim $aResult[1][3] = [['id', 'en', 'fr']]
$iError = 1
$sFileXml = @ScriptDir & '\test.xml'
$sNewText = ''
$hFile = FileOpen($sFileXml, FileGetEncoding($sFileXml))
If $hFile = -1 Then
	MsgBox(16, 'Error', 'FileOpen')
	Exit
EndIf
$sRead = FileRead($hFile)
FileClose($hFile)
$aTemp = StringRegExp($sRead, '(?s)<Text(.*?)</Text>', 3)
If @error Then
	MsgBox(16, 'Error', 'StringRegExp')
	Exit
EndIf
For $i = 1 To 1
	For $i = 1 To UBound($aTemp)
		For $j = 0 To UBound($aResult, 2) - 1
			$sValue = StringRegExpReplace($aTemp[$i - 1], '(?s).*?' & $aResult[0][$j] & '.*?="(.*?)"\W?.*', '$1')
			If @extended <> 1 Then ExitLoop 3
			If $j Then
				$sNewText &= $aResult[0][$j] & '=' & $sValue & @CRLF
			Else
				$sNewText &= '[' & $sValue & ']' & @CRLF
			EndIf
		Next
	Next
	$hFile = FileOpen($sFileXml & '.ini', 2)
	If $hFile = -1 Then ExitLoop
	If Not FileWrite($hFile, StringTrimRight($sNewText, 2)) Then ExitLoop
	FileClose($hFile)
	$iError = 0
Next
If $iError Then
	MsgBox(16, 'Error', 'Error')
Else
	MsgBox(64, 'Info', 'OK')
EndIf


Не знаю, правильно или нет, но у меня работает:
Код:
#include '_XMLDomWrapper.au3'
#include <Array.au3>

$sTexts = '/XnaContent/Texts/Text'
Dim $aName[1], $aValue[1], $aResult[1][1]

$GroupXML = _XMLFileOpen(@ScriptDir & '\test.xml')
$iCount = _XMLGetNodeCount($sTexts)
$aText = _XMLGetChildText($sTexts)
ReDim $aResult[$iCount + 1][$aText[0] + 1]
For $i = 1 To $aText[0]
	$aResult[0][$i] = $aText[$i]
Next
For $i = 1 To $iCount
	_XMLGetAllAttribIndex($sTexts, $aName, $aValue, '', $i - 1)
	If Not $aResult[0][0] Then
		$aResult[0][0] = $aName[0]
	EndIf
	$aResult[$i][0] = $aValue[0]
	For $j = 1 To $aText[0]
		_XMLGetAllAttribIndex($sTexts & '/' & $aText[$j], $aName, $aValue, '', $i - 1)
		$aResult[$i][$j] = $aValue[0]
	Next
Next
_ArrayDisplay($aResult)
 
Верх