tardezyx
Goto Top

Mittels Batch mehrere Einträge aus mehreren XML-Dateien in eine Textdatei schreiben

aus mehreren Dateien (identische Grammatik) mit unterschiedlichen Inhalten sollen mehrere Einträge an unterschiedlichen Positionen (aber innerhalb identischer Tags) in einer separaten neuen Datei übersichtlich gespeichert werden

Hallo,

eigentlich steht das Problem schon im Titel ;)

Ich habe mehrere tausend XML-Dateien, die der gleichen Grammatik folgen und würde gerne bestimmte Informationen daraus in eine einzige Textdatei schreiben, wobei die einzelnen Einträge mittels selbst definiertem Trennzeichen (Tabulator oder Semikolon ist erstmal egal - dient zum späteren Datenimport in Excel) in eine Zeile geschrieben werden sollen und für jede XML-Datei genau 1 Zeile belegt werden soll.

Die XML-Dateien befinden sich darüber hinaus in Unterordnern, so dass man die unterschiedlichen Pfade in den Batch-Aufruf integrieren müsste. Es ist mir aber bereits gelungen, sämtliche Pfade zu den .nfo-Dateien vorab in einer Datei mittels diesem Code abzulegen, der wiederum mittels der Datei list_all_nfos.bat gestartet wird (diese könnte man also in die Batch-Prozedur integrieren):

dir /B /s *.nfo > nfo-list.txt

Die XML-Dateien seien wiederum wie folgt aufgebaut ("..." ist unwichtiges Zeugs):

<?xml version="1.0" encoding="utf-8"?>  
<data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">  
  <title>Dies ist ein Titel</title>
  <year>2006</year>
  <durance>5.3</durance>
  <members>1,217</members>
  <lead>Alfred / Josef / Mike</lead>
  ...
  <extract>
    <details>
      <astate>
        <hidden>stealth</hidden>
        ...
      </astate>
      <vstate>
        <hidden>block</hidden>
        ...
      </vstate>
    </details>
  </extract>
</data>

Herauskommen soll hierbei die folgende Zeile (Tabulatorenabstand):

Dies ist ein Titel	2006	5.3	Alfred / Josef / Mike	stealth	block

Danach soll automatisch ein Zeilenumbruch eingefügt und die nächste .nfo-Datei geparst werden, bis das Programm keine weiteren findet (also die nfo-list.txt am Ende angekommen ist).

Wichtig hierbei ist, dass die herauszuziehenden Informationen bei allen Dateien immer zwischen denselben Tags liegen (alle XML-Dateien unterliegen derselben Grammatik), d.h. der Titel liegt immer zwischen <title> und </title>, das Jahr immer zwischen <year> und </year> usw., aber wo sich diese Tags konkret befinden, ist von Datei zu Datei unterschiedlich, da sich jeweils eine weitere Menge an unterschiedlichen Informationen darin befinden. Außerdem kommen die meisten gesuchten Tags nur 1x pro Datei vor, d.h. es gibt nur einen Titel und nur ein Jahr usw., aber es gibt 2x den <hidden> & </hidden> Tag, so dass man dort den vorangehenden Tag mit einbeziehen müsste - also <astate> & <vstate>.


Wäre das überhaupt mittels Batch-Programmierung umzusetzen?

Danke und Gruß!

Content-ID: 141792

Url: https://administrator.de/contentid/141792

Ausgedruckt am: 26.11.2024 um 14:11 Uhr

bastla
bastla 30.04.2010 um 00:15:57 Uhr
Goto Top
Hallo tardezyx und willkommen im Forum!

Soferne es in den Daten kein "!" gibt und die Reihenfolge von "<astate>" und "<vstate>" immer gleich ist, könnte das so gehen:
@echo off & setlocal enabledelayedexpansion
set "Basis=D:\Startordner"  
set "CSV=Gesamt.csv"  
set "Delim=;"  

pushd "%Basis%"  
del "%CSV%" 2>nul  
for /f "delims=" %%i in ('dir /s /b *.nfo') do (  
    set "Record="  
    for %%f in ("<title>" "<year>" "<durance>" "<lead>" "<hidden>") do (  
        for /f "tokens=3 delims=<>" %%v in ('findstr %%f %%i') do set "Record=!Record!%Delim%%%v"  
    )
    >>"%CSV%" echo !Record:~1!  
)
popd
Allerdings solltest Du keine Geschwindigkeitsrekorde erwarten ...

Grüße
bastla
tardezyx
tardezyx 30.04.2010 um 00:48:20 Uhr
Goto Top
Hallo bastla,

danke! Das sieht schon sehr gut aus - doch haut noch nicht ganz hin, weil ich dir die Ordnerstruktur vorenthalten habe, wie ich grad merke ;)

Die Verzeichnisse und Dateinamen enthalten Leerzeichen, so dass er sie bei dem jetzigen Code nicht findet. Er sucht statt nach Datei "abcd xy (2003).nfo" jetzt nach den Dateien "abcd", "xy" und "(2003).nfo". Wahrscheinlich sucht er auch in den falschen Verzeichnissen, da diese ähnlich benannt sind. Dann würde es wunderbar hinhauen.

Dann würde mich noch interessieren, ob es möglich ist, nach dem Eintrag in den <hidden> - Tags einen weiteren folgen zu lassen, allerdings mit einem anderen Trennzeichen (Doppelpunkt und Leerzeichen?), so dass beide Einträge später dann in Excel in derselben Spalte übernommen werden. Diese Einträge befinden sich nach den <hidden> - Tags zwischen den <movement> - Tags bzw. den <size> - Tags (also Reihenfolge stimmt hier auch - kann man die an diesem auch Punkt umkehren? - das wäre wesentlich besser).

Also XML derart:

(...alles wie oben...)
  <extract>
    <details>
      <astate>
        <hidden>stealth</hidden>
        <movement>move</movement>
        ...
      </astate>
      <astate>
        <hidden>stealth2</hidden>
        <movement>move2</movement>
        ...
      </astate>
      <vstate>
        <hidden>block</hidden>
        ...
        <size>large</size>
      </vstate>
    </details>
  </extract>
</data>

So dass es dann in der CSV so aussieht:

Dies ist ein Titel;2006;5.3;Alfred / Josef / Mike;stealth: move;stealth2: move2;block: large

Danke nochmal und Gruß!

EDIT: Was ist, wenn Ausrufezeichen in den Daten auftauchen? Das ist in der Mehrzahl der Dateien der Fall.

EDIT 2: Leerzeichenproblem erledigt: habe einfach zwei " in diese Zeilen eingefügt:

for /f "tokens=3 delims=<>" %%v in ('findstr %%f "%%i"') do set "Record=!Record!%Delim%%%v"
bastla
bastla 30.04.2010 um 01:30:20 Uhr
Goto Top
Hallo tardezyx!

Hinsichtlich der Leerzeichen in den Verzeichnis-/Dateinamen trifft mich eine Mitschuld face-sad - üblicher Weise berücksichtige ich das bereits vorsorglich (indem in Zeile 11 "%%i" unter Anführungszeichen gesetzt wird [Edit] hatte Dein EDIT 2 noch nicht gesehen [/Edit]); das hatte ich zwar in meiner ersten Version (welche mit Unterprogrammen arbeitete) auch getan - beim Versuch, die Performance zu verbessern, indem ich auf die Variante mit geschachtelten Schleifen umstellte, sind die Anführungszeichen dann leider auf der Strecke geblieben ...

... umso ärgerlicher, da ich nun (wegen der Ausrufezeichen) ohnehin wieder Unterprogramme verwenden muss.

Ohne Berücksichtigung der Zusatzanforderung hinsichtlich der "<movement>"- und "<size>"-Tags sähe das dann zunächst so aus:
@echo off & setlocal
set "Basis=D:\Startordner"  
set "CSV=Gesamt.csv"  
set "Delim=;"  

pushd "%Basis%"  
del "%CSV%" 2>nul  
for /f "delims=" %%i in ('dir /s /b *.nfo') do call :ProcessFile "%%i"  
popd
goto :eof

:ProcessFile
set "Record="  
for %%f in ("<title>" "<year>" "<durance>" "<lead>" "<hidden>") do for /f "tokens=3 delims=<>" %%v in ('findstr %%f %1') do call :ProcessField "%%v"  
>>"%CSV%" echo %Record:~1%  
goto :eof

:ProcessField
set "Record=%Record%%Delim%%~1"  
goto :eof
Bis hierher war es einfach möglich, die einzelnen Tags in der gewünschten Reihenfolge mit "findstr" jeweils aus der übergebenen Datei heraussuchen zu lassen (und zu einer Zeile zusammenzufügen) - um die zwischen den "<hidden>"-Werten liegenden Tags zu berücksichtigen, wird es allerdings erforderlich sein, die gesamte Datei zeilenweise durchzugehen.

Da es inzwischen schon etwas spät (bzw früh face-wink) ist, muss ich Dich in dieser Hinsicht leider auf später am Tag vertrösten ...

Grüße
bastla
tardezyx
tardezyx 30.04.2010 um 02:03:29 Uhr
Goto Top
Hey, spitze - genau so! Danke dir wie Sau! ;)

Gut, dass du dich noch nicht weiter rangesetzt hast.

Jetzt, wo ich das so sehe, wäre es vorteilhafter, dass er das <hidden> nur aus dem <vstate> holt und danach, was in der <size> steht. Es steht aber in den XML fast immer noch was zwischen <vstate> und <hidden>.

Das sieht in der XML so aus:

  ...
  <extract>
    <details>
      <astate>
        ...
      </astate>
      <astate>
        ...
      </astate>
      <vstate>
        ...
        <hidden>block</hidden>
        ...
        <size>large</size>
      </vstate>
    </details>
  </extract>
</data>

Ansonsten alles wie gehabt. Bestimmt knifflig.

Das mit den Doppelpunkten wäre erstmal egal. Falls es einfach gänge, kannst du es mir ja trotzdem noch zeigen. Vielleicht brauch ich's mal.

Guts Nächtle ;)

EDIT: Oje, jetzt hat er Probleme mit "&" im Dateiname und sagt: "Der Befehl "amp" ist entweder falsch geschrieben oder konnte nicht gefunden werden."
bastla
bastla 30.04.2010 um 02:18:00 Uhr
Goto Top
Hallo tardezyx!

Wenn nur das letzte "<hidden>" benötigt wird und es "<size>" nur einmal geben sollte, könnte sich das doch jetzt noch ausgehen face-wink - versuch es mit folgendem (ungetesten) Ersatz des Teiles ab Zeile 12:
:ProcessFile
set "Record="  
for %%f in ("<title>" "<year>" "<durance>" "<lead>") do for /f "tokens=3 delims=<>" %%v in ('findstr %%f %1') do call :ProcessField "%%v"  
set "Hidden="  
for /f "tokens=3 delims=<>" %%v in ('findstr "<hidden>" %1') do set "Hidden=%%v"  
for /f "tokens=3 delims=<>" %%v in ('findstr "<size>" %1') do set "Hidden=%Hidden%: %%v"  
>>"%CSV%" echo %Record%%Hidden%  
goto :eof

:ProcessField
set "Record=%Record%%~1%Delim%"  
goto :eof
Grüße
bastla
tardezyx
tardezyx 30.04.2010 um 10:23:40 Uhr
Goto Top
Hallo bastla,

super!

Jetzt ist nur noch das Amp-Problem vorhanden, also das "&" im <title>. Falls es im Verzeichnis- und Dateinamen auftaucht, macht es keine Probleme, aber er hört sofort im <title> (dieser entspricht meist dem Dateinamen) beim "&" auf und geht zur nächsten .nfo-Datei.

Das Ergebnis sieht dann so aus:

Dies ist ein Titel;2006;5.3;Alfred / Josef / Mike;block: large
Bonnie
Das ist auch ein Titel;2003;6.9;Fred / Harald;block: small

Bonnie wäre eigentlich bspw. Bonnie & Clyde.

Gruß!
bastla
bastla 30.04.2010 um 10:52:31 Uhr
Goto Top
Hallo tardezyx!

Dann spendieren wir dem Batch eben noch eine Zeile 11a:
set "Record=%Record:&=^&%"
Grüße
bastla
tardezyx
tardezyx 30.04.2010 um 11:22:45 Uhr
Goto Top
Hallo,

da kommt derselbe Fehler und er macht jetzt aus Bonnie & Clyde:

Bonnie ^^

Und geht wieder auf die nächste Zeile.

Gruß!
bastla
bastla 30.04.2010 um 11:32:45 Uhr
Goto Top
Hallo tardezyx!

Nur zur Sicherheit: Das gesamte Unterprogramm sieht jetzt so aus?
:ProcessField
set "Record=%Record%%~1%Delim%"  
set "Record=%Record:&=^&%"  
goto :eof
Grüße
bastla
tardezyx
tardezyx 30.04.2010 um 11:41:52 Uhr
Goto Top
Jap. Ich nutze Windows 7 x64 Pro.

Er sagt immer noch: "Der Befehl "amp" ist entweder falsch geschrieben oder konnte nicht gefunden werden."

Gruß!
bastla
bastla 30.04.2010 um 12:43:51 Uhr
Goto Top
Hallo tardezyx!

Wenn ja ohnehin ein Import der Daten in Excel geplant ist, sollte es ja kein Problem sein, wenn die Felder unter Anführungszeichen stehen - versuch es daher mal mit dieser Variante:
:ProcessField
set Record=%Record%%1%Delim%
goto :eof
Grüße
bastla
tardezyx
tardezyx 30.04.2010 um 13:29:37 Uhr
Goto Top
Alles klar - danke dir nochmal!

Gruß!
tardezyx
tardezyx 30.04.2010 um 17:14:28 Uhr
Goto Top
Hallo nochmal,

ich probiere gerade herum, so dass er mir während dieses Prozesses den gerade ablaufenden Titel anzeigt, den er in der Datei findet. Ich habe es hinbekommen, dass er mir die Datei anzeigt, der Titel wäre noch besser.

Ich habe da nur die 3. Zeile eingefügt:

:ProcessFile
set "Record="  
for %%f in ("<title>") do echo %%i  

%%i ist ja offensichtlich der Dateiname? Welche Variable ist aber der Inhalt von <title>?

Danke und Gruß!
bastla
bastla 30.04.2010 um 17:23:30 Uhr
Goto Top
Hallo tardezyx!
%%i ist ja offensichtlich der Dateiname? Welche Variable ist aber der Inhalt von <title>?
Der "title", aber auch alle anderen in der Schleife ausgelesenen Felder, wird als "%%v" an das Unterprogramm ":ProcessField" übergeben und kommt dort als %1 (unter Anführungszeichen stehend) an - da der Titel das erste Feld des Datensatzes darstellt (vorher ist die entsprechende Variable "leer" bzw genauer: "not defined"), könntest Du unmittelbar nach ":ProcessField" die folgende Zeile einfügen:
if not defined Record echo %1
Grüße
bastla
tardezyx
tardezyx 30.04.2010 um 17:39:04 Uhr
Goto Top
Danke, klappt!

Jetzt noch 'ne schöne GUI rundrum und wir sind fertig - Spässle ;)

Gruß!