servomonitor
Goto Top

Aus Textdatei 4 nachfolgende Zeilen für mehrere Suchbegriffe in neue Datei schreiben

Hallo,
in einer Textdatei sind Werte im xml-Format gespeichert. Die einzelnen Parameter sind jeweils 5 Zeilen lang. In der ersten und dritten Zeile ist jeweils die Parameternummer hinterlegt. Zur einfachen Prüfung bestimmter Parameter möchte ich bestimmte Parameter in eine neue Textdatei schreiben.
Das Finden mittels findstr für liefert auch genau die beiden Zeilennummern z.B. 1244 und 1246. Wie kann man mittels Batch nur die Zeilen 1244-1248 in eine neue Textdatei schreiben? Alle bisher gefundenen Ansätze schreiben immer den Rest der Quell-Datei in die neue Datei. Das ganze sollte dann auch noch für ca. 15 andere Parameternummern erfolgen. Jeder Parameter tritt nur einmal in der Datei auf.

servomonitor

Content-Key: 60004

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

Printed on: April 19, 2024 at 08:04 o'clock

Member: bastla
bastla May 28, 2007 at 22:35:46 (UTC)
Goto Top
Hallo servomonitor und willkommen im Forum!

Ein erster Ansatz könnte so aussehen:
@echo off & setlocal
set "Quelle=D:\Quelle.xml"  
set "Ziel=D:\Ziel.xml"  
set "Param=D:\Param.txt"  
del "%Ziel%" 2>nul  
set /a Zeile=0
for /f "usebackq delims=" %%i in ("%Quelle%") do call :ProcessLine "%%i"  
goto :eof
:ProcessLine
set "Text=%~1"  
set "Text=%Text:<=^<%"  
set "Text=%Text:>=^>%"  
set "Text=%Text:&=^&%"  
if %Zeile% neq 0 goto :Schreiben
:Check
echo "%Text%"|findstr /c:"%%p" /g:"%Param%">nul && set /a Zeile=1 && echo %Text%>>"%Ziel%"  
goto :eof
:Schreiben
set /a Zeile+=1
if %Zeile% gtr 5 set /a Zeile=0 & goto :Check
echo %Text%>>"%Ziel%"  
goto :eof
Anpassen musst Du die Pfade für Quell-, Ziel- und Parameterdatei. In der Parameterdatei sind die gesuchten Parameter anzuführen, wobei jeder Parameter in einer eigenen Zeile stehen muss.

Da Du den Inhalt Deiner Quell-Datei nur schematisch beschrieben hast, sind vorläufig nur die Zeichen "<" und ">" als "Batch-Sonderzeichen" berücksichtigt. Sollten daneben auch zB "&" oder "|" vorkommen, müssten diese (siehe "set"-Zeilen nach ":ProcessLine") in weiteren Zeilen ebenfalls mit "^" maskiert werden - Beispiel für "&":
set "Text=%Text:&=^&%"  
[Edit] Aufgrund der im Kommentar unten von servomonitor geposteten xml-Datei "&"-Umwandlung oben eingearbeitet. [/Edit]
[Edit2] "Check"-Routine verbessert. [/Edit2]


Grüße
bastla
Member: Biber
Biber May 29, 2007 at 05:46:16 (UTC)
Goto Top
Moin servomonitor,

willkommen im Forum auch von mir.

Eine Bitte hätte ich noch an Dich:
Auch wenn bastla und noch ein paar Skript-Künstler hier die von plastisch beschriebene XML-Datei bildhaft vor Augen sehen: Für diejenigen im Forum, die sowohl Problem wie auch Lösung nachvollziehbar [oder: Copy&Pastable] sehen wollen, wäre es nett eine (Beispiel-) XML-Datei oben in Deiner Eröffnungsfrage zu ergänzen.

Wäre das möglich?
Danke

Biber
Member: servomonitor
servomonitor May 29, 2007 at 07:49:49 (UTC)
Goto Top
@bastla, vielen Dank mal für die Batch, ich werde diese testen, komme aber frühestens heute Nachmittag dazu.
@Biber: Hier nun der Anfang und das Ende der Datei. Die Parameter dazwischen unterscheiden sich im innerhalb der Datei durch eindeutige Parameternamen. Zeile 1 des Parameters beginnt mit <Symbol Name gefolgt vom Parameternamen (z.B. r2, p5), der Wert des Parameters steht in der zweiten Zeile
Meine Idee ist es, einige der Parameter anhand der Parameternummer in eine neue Datei zu schreiben, damit man diese relativ schnell überprüfen kann. Btw es handelt sich bei der Datei um Parameterfile für einen Motorregler.

servomonitor
<?xml version = '1.0' encoding = 'windows-1252' standalone = 'yes'?> 
<?xml-stylesheet type="text/xsl" href="umc_data.xsl"?> 

<!--The following Code declares the XML-Data-Syntax: -->
<!--End Syntaxdeclaration-->

<!--Special characters: &amp = &, &lt = <, &gt = >, &quot = " --> 


<!--XML Data:-->


<ISymbol>
   <Symbol Name = "r2" UDLSymbolIsActive = "True"> 
      <Value Type = "VT_I2" Value = "45"/> 
      <ESSymbolDescr Name = "r2" SymbolClass = "1"/> 
      <Array Array = "False" ArrayElements = "0"/> 
   </Symbol>
   <Symbol Name = "p5" UDLSymbolIsActive = "True"> 
      <Value Type = "VT_UI2" Value = "2"/> 
      <ESSymbolDescr Name = "p5" SymbolClass = "0"/> 
      <Array Array = "False" ArrayElements = "0"/> 
   </Symbol>
   <Symbol Name = "p6" UDLSymbolIsActive = "True"> 
      <Value Type = "VT_I2" Value = "4"/> 
      <ESSymbolDescr Name = "p6" SymbolClass = "1"/> 
      <Array Array = "False" ArrayElements = "0"/> 
   </Symbol>

#### Hier folgen weitere Parameter#####
#### bis zum Ende der Datei
<Symbol Name = "p9899" UDLSymbolIsActive = "True"> 
      <Value Type = "VT_UI4" Value = "0"/> 
      <ESSymbolDescr Name = "p9899" SymbolClass = "0"/> 
      <Array Array = "False" ArrayElements = "0"/> 
   </Symbol>
</ISymbol>
<!--End XML-Data-->
Member: bastla
bastla May 29, 2007 at 08:00:22 (UTC)
Goto Top
Hallo servomonitor!

Wie Deine Testdaten zeigen, ist zumindest auch das Zeichen "&" zu berücksichtigen (und inzwischen oben im Batch auch eingearbeitet) - bitte zum Testen daher die aktualisierte Version verwenden.

Grüße
bastla
Member: bastla
bastla May 29, 2007 at 08:18:44 (UTC)
Goto Top
... Nachtrag:

Eine "Param.txt" zu Deinem obigen Beispiel könnte etwa so aussehen:
"p5"  
"r2"  
"p9899"  
Anmerkung zu den Anführungszeichen: Damit stellst Du sicher, dass exakte Übereinstimmung der Symbol-Namen vorliegen muss - eine Schreibweise r2 (also ohne Anführungszeichen) würde zB auch die Daten von "r25", "r237" oder "r2106" liefern - es würde also genügen, dass die Zeichenfolge r2 nur enthalten ist. Diese Möglichkeit könntest Du aber auch gezielt einsetzen, um zB mit r247 alle Symbole von r2470 bis r2479 (aber natürlich auch r247) anzusprechen.

Die Reihenfolge in der Zieldatei hängt übrigens von jener in der Quelldatei, nicht von der Reihenfolge in der "Param.txt" ab.

Grüße
bastla

[Edit] Schreibweise der Suchparameter genauer dargestellt. [/Edit]
Member: servomonitor
servomonitor May 29, 2007 at 13:57:12 (UTC)
Goto Top
@bastla, das klappt schon ganz gut. In der Datei sind allerdings auch Zahlenwerte mit Nachkommastellen. Diese werden unabhängig von der gesuchten Parameternummer bis zum Komma in die Ausgabedatei geschrieben, sind also nicht Teil der Suchliste. Der Nachkommateil und der Rest der Zeile geht aber verloren. Das Komma unterbricht also irgendwie die Verarbeitung. Gibt es hierfür ein Gegenmittel?

servomonitor

In der Quelldatei steht zum Beispiel:
<Symbol Name = "r26" UDLSymbolIsActive = "True">
<Value Type = "VT_R4" Value = "540,145"/>
<ESSymbolDescr Name = "r26" SymbolClass = "0"/>
<Array Array = "False" ArrayElements = "0"/>
</Symbol>

In der Ausgabedatei erscheint dann:
" ^<Value Type = "VT_R4" Value = "540"|findstr /c:"%p" /g:"C:\test\Param.txt">nul && set /a Zeile=1 && echo ^<Value Type = "VT_R4" Value = "540
obwohl r26 nicht als Suchoption angegeben ist.
Member: bastla
bastla May 29, 2007 at 14:40:42 (UTC)
Goto Top
Hallo servomonitor!

Auf die Schnelle fällt mir nix Besseres ein, als die Sprache zu wechseln - wenn Du keine grundsätzlichen Einwände gegen VB-Script hast, versuch es einmal damit:
Const sQuelle = "D:\Quelle.xml"  
Const sZiel = "D:\Ziel.xml"  
Const sParam = "D:\Param.txt"  
Set fso = CreateObject("Scripting.FileSystemObject")  
If Not fso.FileExists(sQuelle) Then
	WScript.Echo "Quelldatei " & sQuelle & " nicht gefunden!"  
	WScript.Quit(1)
End If
If Not fso.FileExists(sParam) Then
	WScript.Echo "Parameterdatei " & sParam & " nicht gefunden!"  
	WScript.Quit(1)
End If
aParams = Split(fso.OpenTextFile(sParam, 1).ReadAll, vbCrLF)
Set oQuelle = fso.OpenTextFile(sQuelle, 1)
Set oZiel = fso.OpenTextFile(sZiel, 2, True)
Do While Not oQuelle.AtEndOfStream
	sText = oQuelle.ReadLine
	For i = 0 To UBound(aParams)
		If InStr(sText, aParams(i)) Then
			oZiel.WriteLine sText
			For j = 2 To 5
				If Not oQuelle.AtEndOfStream Then
					oZiel.WriteLine oQuelle.ReadLine
				Else
					WScript.Echo "Nicht genügend Datenzeilen für " & aParams(i)  
				End If
			Next
			Exit For
		End If
	Next
Loop
oQuelle.Close
oZiel.Close
WScript.Echo "Done."  
Speichern unter zB "Filtern.vbs" und per Doppelklick starten.

Grüße
bastla
Member: servomonitor
servomonitor May 29, 2007 at 15:29:05 (UTC)
Goto Top
@bastla, das Skript erzeugt bei mir eine 1:1-Kopie der Quelldatei. Ich bekomme zweimal eine Msgbox "Nicht genügend Speicher für" ohne weitere Angabe. Nach zweimal OK erscheint dann "Done". Ergänzende Info: Die Quelldatei hat 14406 Zeilen. Das Einlesen in einem Rutsch könnte deshalb kritisch sein.
Woran scheitert eigentlich der Commandline-Ansatz? Welches Kommando sieht der Interpreter bei einem Komma?
servomonitor
Member: bastla
bastla May 29, 2007 at 16:48:35 (UTC)
Goto Top
Hallo servomonitor!

Zum VB-Script: Stelle bitte sicher, dass es in der "Param.txt" keine Leerzeile (am Ende) gibt, da sonst jeder Vergleich eine "Übereinstimmung" liefert. Falls die Fehlermeldung "Nicht genügend Datenzeilen für" lauten sollte, wäre das ein Indiz für meine Vermutung.

Woran scheitert eigentlich der Commandline-Ansatz? Welches Kommando sieht der Interpreter bei einem Komma?
Kann ich auch noch nicht sagen; Tatsache ist, dass die Zeile dem Unterprogramm ":ProcessLine" zwar richtig übergeben wird, bei der Übernahme des Argumentes %1 aber beim Komma getrennt wird, wodurch bei Deinem Beispiel 145"/>" als %2 übergeben wird.

Vielleicht hat Biber eine Idee dazu ...

Grüße
bastla

[Edit] Erklärungsansatz: Durch das "Zerschneiden" des Arguments ergibt sich eine ungerade Anzahl von Anführungszeichen, sodass das "echo" (in der Zeile nach ":Check") auch den Rest der Zeile als auszugebenden Text interpretiert. [/Edit]
Member: Biber
Biber May 29, 2007 at 18:42:48 (UTC)
Goto Top
Moin servomonitor und bastla,

ich stimme soweit mit bastlas Erklärungsansatz überein - hatte allerdings diesen Fall auch noch nicht (eine Kombination von XML-Tags und Text-in-Anführungszeichen-im-Text und Kommata)...

Anyhow - im Batch gibt es ja bekanntermaßen eine Lösung für (fast) alles.

bastlas CMD-Ansatz braucht nur winzige Anpassungen:
@echo off & setlocal
set "Quelle=D:\Quelle.xml"  
set "Ziel=D:\Ziel.xml"  

<b>set "Ziel=Con:"</b>   

del "%Ziel%" 2>nul  
set /a Zeile=0
for /f "usebackq <b>skip=10</b> delims=" %%i in ("%Quelle%") do call :ProcessLine "%%i"  
goto :eof
:ProcessLine
<b>Set text=%*
Set "Text=%Text:~1,-1%"</b>  
set "Text=%Text:<=^<%"  
set "Text=%Text:>=^>%"  
set "Text=%Text:&=^&%"  

if %Zeile% neq 0 goto :Schreiben
:Check
echo "%Text%"|findstr /c:"%%p" /g:"%Param%">nul && set /a Zeile=1 && echo %Text%>>"%Ziel%"  
goto :eof
~~~~~~
:Schreiben
set /a Zeile+=1
if %Zeile% gtr 5 set /a Zeile=0 & goto :Check
echo %Text%>>"%Ziel%"  
goto :eof

Der drei fett hervorgehobenen Stellen:

1. set "Ziel=Con:" --natürlich zum Testen nur auf den Monitor.

2. skip=10 die ersten 10 (oder so) Zeilen der XML-Datei, den Header kann ich ignorieren.
Sonst wird mir die Kommentarzeile [ "<!-- Special Characters...."] mit den vielen Kommata und den vielen Sonderzeichen mit ausgegeben.
Hab ich nicht näher untersucht warum... da fand ich das Skippen einfacher.

3. Set text=%*
Set "Text=%Text:~1,-1%"

Ich nehme nicht den ersten (und eigentlich einzigen Parameter) sondern "alle". *g
wenn ich "alle" übernehme und weiß, dass bei alle als erstes und letztes Zeichen ein " kommen MUSS... dann entsorge ich diese beiden Zeichen.


Das Ergebnis mit meiner aus den obigen Vorlagen zusammengestoppelten XML-Datei:

>TestServomonitorsXMLGeraffel.bat
   <Symbol Name = "p5" UDLSymbolIsActive = "True">  
      <Value Type = "VT_UI2" Value = "2"/>  
      <ESSymbolDescr Name = "p5" SymbolClass = "0"/>  
      <Array Array = "False" ArrayElements = "0"/>  
   </Symbol>
<Symbol Name = "r26" UDLSymbolIsActive = "True">  
<b><Value Type = "VT_R4" Value = "540,145"/></b>  
<ESSymbolDescr Name = "r26" SymbolClass = "0"/>  
<Array Array = "False" ArrayElements = "0"/>  
</Symbol>
<Symbol Name = "p9899" UDLSymbolIsActive = "True">  
      <Value Type = "VT_UI4" Value = "0"/>  
      <ESSymbolDescr Name = "p9899" SymbolClass = "0"/>  
      <Array Array = "False" ArrayElements = "0"/>  
   </Symbol>

..as should do...

Aber dennoch... ist nicht sonderlich vollständig getestet: ob das nun beliebig viele Kommata verdaut... niemand weiß....
Oder anders formuliert: Für diesen Zweck ist Batch nicht das optimale Werkzeug.

Natürlich würde mich trotzdem interessieren, servomonitor, ob dieser bastla-variiert-von-Biber-Schnipsel deine ganze XML-Datei vertragen kann.

Gruß
Biber
Member: bastla
bastla May 29, 2007 at 19:12:42 (UTC)
Goto Top
@servomonitor
... und ob's hinsichtlich der VBS-Variante tatsächlich an der "Param.txt" lag ...

@Biber
Die Idee hatte ich zwar, aber %* wollte mir beim besten Willen nicht einfallen (obwohl ich es schon einmal bei einem Deiner OEvres gesehen hatte).

Grüße
bastla
Member: servomonitor
servomonitor May 30, 2007 at 06:21:36 (UTC)
Goto Top
@Biber
Die Batch funktioniert wunderbar, dauert zwar etwas länger als das VBS-Skript, aber es geht. Genau das hatte ich mit etwas weniger - oder auch mehr - Zeilen nie hingekriegt. Vielen Dank für die geniale Idee mit dem *.

@bastla
Die VBS funktioniert auch. Es lag an der Leerzeile am Ende der param.txt. Wie gesagt, ist das VBS schneller.

Das Ergebnis der beiden Skripte ist in jedem einzelnen Zeichen 1:1 identisch, so daß ich nur staunend Danke sagen kann. Das war sehr interessant und lehrreich für mich. Ich werde allerdings noch etwas Zeit brauchen, bis ich die verwendeten Befehle verstanden habe.

servomonitor
Member: bastla
bastla May 30, 2007 at 11:39:02 (UTC)
Goto Top
Hallo servomonitor!

Freut mich, dass es klappt.

Ich werde allerdings noch etwas Zeit brauchen, bis ich die verwendeten Befehle verstanden habe.
Ich muss zugeben, dass ich sehr sparsam kommentiert habe face-wink, werde aber gerne noch verbleibende Fragen zu klären versuchen. Wenn Du's selbst rausbekommst, ist es natürlich noch besser ...

Grüße
bastla
Member: Biber
Biber May 30, 2007 at 12:43:02 (UTC)
Goto Top
Sagen wir so...

Wenn bastla und ich auch noch kommentiert hätten, dann wäre es ja ein Tutorial geworden.. *gg

Auch ich stehe natürlich für Nachfragen zur Verügung.

Allerdings werde ich, falls hier im Beitrag in den nächsten paar Tagen keine Aktivitäten mehr zu verzeichnen sind, diesen ja bereits "gelösten" Beitrag schließen.

Grundsätzlich ist diese spezielle Konstellation von Zeilen in einer Inputdatei auch so exotisch, dass ich gar nicht wüsste,
unter welchem Stichwort ich so etwas "wiederfindbar" ablegen sollte.

"Wenn von einer FOR-Anweisung nur ein Parameter als String in Anführungszeichen an einen CALL-Block übergeben wird,
dort aber mehrere Parameter ankommen, weil im übergebenen String Kommas enthalten sind, dann..."

---> Hört sich ziemlich speziell an.....wäre nicht der Bringer auf einer Grillparty face-wink

Grüße
Biber