many
Goto Top

Substring-Verarbeitung in Batch-Scripts

Hallo zusammen,

ich möchte aus einer Ascii-Datei den 1. Satz mit variablen Satz- und Feldlängen einlesen. Mein Problem dabei ist, dass es keine festen Feldlängen gibt und dass manche Felder optional sind.
Genauer:
- Der Satz gliedert sich in Blöcke, die jeweils mit einem Header ZAA, ZAB oder ZAC beginnen.
- Der Block ZAA ist optional, muss also nicht immer vorhanden sein (3. Bsp.).
- Der Block ZAB wird durch das Trennzeichen '+' in mehrere Segmente aufgegliedert.
- Der Block ZAC und evt. weitere sind uninteressant.
- Ein Segment (zwischen zwei '+'-Zeichen) kann aus 0 - 2 Feldern bestehen. Bei 2 Feldern
wird als Trennzeichen ein ':' benutzt.
- Frage: Wie weise ich die Felder Variablen zu?

1. Beispiel:
ZAA+001ZAB+ABC:1+4711:01+4721:05+060321:1021+123++1++ZAC+++...

2. Beispiel:
ZAA+002ZAB+DEF:2+4712:02+4722:06+060322:1022+456++++ZAC+++...

3. Beispiel:
ZAB+GHI:3+4713:03+4723:07+060323:1023+789++++ZAC+++...

Meine Kenntnisse über For-Schleifen, Delims und String-Verarbeitung reichen für diese
Fragestellung leider nicht aus. Hat jemand eine Idee?

Vielen Dank

Many

Content-Key: 30395

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

Printed on: April 23, 2024 at 14:04 o'clock

Member: Biber
Biber Apr 13, 2006 at 19:48:50 (UTC)
Goto Top
Moin many,

vielen Dank für die exakte und vollständige Beschreibung des Inputs *gg ...großes Lob von mir!

Ähhm... magst Du auch verraten, wie der Output/die Verarbeitung aussehen soll?
In Variablen speichern ist ja im Batch wie auch in "echten" Programmiersprachen eine vorübergehende Geschichte.

Wo sollen denn welche "Felder" welcher "Satzarten" hin? In neue Dateien geschrieben? Oder in irgendwelche Variablen ZAB_3A.....ZAB_5B oder wie ist der Plan?

Rückfragend
Biber
Member: many
many Apr 13, 2006 at 21:39:00 (UTC)
Goto Top
Moin,

vielen Dank für die schnelle Reaktion. Ich brauche die Felder des ZAB-Blockes in Variablen, um innerhalb des Scripts dann später entsprechend verzweigen zu können.
Vielleicht wird die Satzstruktur etwas anschaulicher, wenn ich einige Strings aus dem 1. Beispiel übersetze:
ABC = Absendername
4711 = Kunden-Nr
060321 = Datum
1021 = Uhrzeit

Schönen Gruß

Many
Member: Biber
Biber Apr 13, 2006 at 22:37:07 (UTC)
Goto Top
Na, Many,
dann fehlen noch folgende Randbedingungen, ehe wir uns leichtfertig auf Batch als geeignetes Werkzeug einschiessen:

- ist die worst case-Länge eines Satzes kurz genug, um sie in einem Batch verarbeiten zu können? Und die Tokenanzahl auch < 55?
Wenn z.B. die ZAA-Zeile auch mal 500 Zeichen lang werden kann, würde ich eher in VB/JScript anfangen als mit Batch.

- die zweite Restriktion: Wenn denn die Feldinhalte abhängig von Ihrer Position bestimmte Bedeutung haben (wenn ich Deinen 2. Post richtig deute), dann sollten und können auch die Variablennamen sprechend sein.
Beispiel:
wenn ZAA-Feld2==ABC = Absendername ->Variablenname ZAA_Absender
wenn ZAA-Feld3==4711 = Kunden-Nr ->Variablenname ZAA_Kunden-Nr
wenn ZAA-Feld4==060321 = Datum ->Variablenname ZAA_Datum
wenn ZAA-Feld5==1021 = ........ZAA_Uhrzeit etc.

..und diese Variablennamen und Feldreihenfolgen sollten natürlich in Strukturbeschreibungsdateien ZAAFields.txt, ZABFields.txt, ZACFields.txt abgelegt sein (also quasi ein Mini-Mini-Data-Dictionary, eine Feldbeschreibung)

Hat u.a. den Vorteil, das sich ggf. auch gezielt nur auf die Felder 1-3, 7 und 14 zugreifen kann, wenn ich nur die brauche.
Ansonsten ist das Zerlegen des Strings auch im Batch kein Hexenwerk...

$cmd$type ZAAtest.txt
ZAA+001ZAB+ABC:1+4711:01+4721:05+060321:1021+123++1++ZAC+++...

$cmd$for /f "delims=+ tokens=1-25" %a in (ZAAtest.txt) do @echo %a %b %c %d %e %f %g %h %i %j
ZAA 001ZAB ABC:1 4711:01 4721:05 060321:1021 123 1 ZAC ...

Die viele Rückfragerei nur, damit wir nicht in die falsche Richtung loslaufen...
Ich stufe aber erstmal den Beitrag runter von "Tutorial" auf "Beitrag" *gg

Gruß
Biber
Member: many
many Apr 15, 2006 at 10:18:58 (UTC)
Goto Top
Moin Biber,

hier die noch fehlenden Infos:
- Die maximale Satzlänge des ersten Satzes wird ca. 150 Byte nicht überschreiten.
- Aus dem 1. Satz brauche ich lediglich Felder aus dem ZAB-Block.
- Nur mit delims=+ zu arbeiten, greift m.E. zu kurz: Die Blöcke beginnen mit einem Header ZAA, ZAB, ZAC... (vgl. Beschreibung oben). Da ich nur den ZAB-Block brauche, könnte ich mir ein Vorgehen wie folgt vorstellen:
- Schmeiß alles weg vor ZAB
- Schmeiß alles weg ab ZAC
- Schmeiß den String ZAB weg
- Übrig bleiben 8 Segment durch + getrennt (hier kommt wohl delims=+ zum Zuge)
- Wenn in einem Segment zwei Felder enthalten sind, sind sie durch : getrennt (also
wohl delims=face-smile
- Der ZAB-Block besteht aus genau 8 Segmenten. Ist dafür eine Strukturbeschreibungsdatei nicht etwas oversized?

Vielen Dank für Dein Engagement. Schönen Gruß

Many
Member: Biber
Biber Apr 15, 2006 at 12:00:29 (UTC)
Goto Top
Moin Many,

so in etwa sähe eine Skizze aus.
:: -------snipp ZAB-Split.bat
@echo off  
:: aus der Textdatei alle Zeilen verwerten, die mit "ZAB" beginnen... nur ZAB-Satzart  
Set "NeededVars=ZAB_1A ZAB_1B ZAB_2A ZAB_2B ZAB_3A ZAB_3B ZAB_3A ZAB_4B"  
Set "NeededVars=%neededVars% ZAB_5A ZAB_5B ZAB_6A ZAB_6B ZAB_7A ZAB7_B ZAB_8A ZAB_8B"  
FOR %%a in ( "%needevars%") do set "%%a="  
:: Die nächsten ECHO Zeilen nur für DEBUG
Echo Dateiinhalt %1:
Type %1
Echo ---------
Echo gesplittete Zeile:
for /f "delims=" %%i in ('findstr /b "ZAB" %1') do echo %%i  
:: Ende von die letzten ECHO-Zeilen nur für DEBUG
for /f "delims=" %%i in ('findstr /b "ZAB" %1') do  call :ZABSplit %%i  
set ZAB
goto :eof
:ZABSplit
If [%1]== goto :eof
Set Thisline=%1
Set thisline=%thisline:ZAC=}%
:: ich ersetze "ZAC" durch ein seltenes Zeichen, weil ich da abschneiden will  
For /f "tokens=1 delims=}" %%i in ("%thisline%") do set "Thisline=%%i"  
:: echo Thisline ohne ZAC+++-Block [%thisline%]
Set thisline=%thisline:++=+$+%
Set thisline=%thisline:++=+$+%
Set thisline=%thisline:++=+$+%
echo Thisline alle "++" durch "+$+"ersetzt [%thisline%]  
For /f "tokens=2-9 Delims=+" %%a in ("%thisline%") do (  
  IF [%%a] NEQ  IF [%%a] NEQ [$] for /f "delims=: tokens=1-2" %%i in (":%%a") do (  
    Set ZAB_1A=%%i & Set ZAB_1B=%%j)
  IF [%%b] NEQ  IF [%%b] NEQ [$] for /f "delims=: tokens=1-2" %%i in (":%%b") do (  
    Set ZAB_2A=%%i & Set ZAB_2B=%%j)
  IF [%%c] NEQ  IF [%%c] NEQ [$] for /f "delims=: tokens=1-2" %%i in (":%%d") do (  
    Set ZAB_3A=%%i & Set ZAB_3B=%%j)
  IF [%%d] NEQ  IF [%%d] NEQ [$] for /f "delims=: tokens=1-2" %%i in (":%%d") do (  
    Set ZAB_4A=%%i & Set ZAB_4B=%%j)
  IF [%%e] NEQ  IF [%%e] NEQ [$] for /f "delims=: tokens=1-2" %%i in (":%%e") do (  
    Set ZAB_5A=%%i & Set ZAB_5B=%%j)
  IF [%%f] NEQ  IF [%%f] NEQ [$] for /f "delims=: tokens=1-2" %%i in (":%%f") do (  
    Set ZAB_6A=%%i & Set ZAB_6B=%%j)
  IF [%%g] NEQ  IF [%%g] NEQ [$] for /f "delims=: tokens=1-2" %%i in (":%%g") do (  
    Set ZAB_7A=%%i & Set ZAB_7B=%%j)
  IF [%%h] NEQ  IF [%%h] NEQ [$] for /f "delims=: tokens=1-2" %%i in (":%%h") do (  
    Set ZAB_8A=%%i & Set ZAB_8B=%%j)
)  
goto :eof
:: --snapp ZABSplit.bat
Ergebnis ist
$cmd$zabsplit zaatest.txt
Dateiinhalt zaatest.txt:
;1. Beispiel...
ZAA+001ZAB+ABC:1+4711:01+4721:05+060321:1021+123++1++ZAC+++...
;2. Beispiel:
ZAA+002ZAB+DEF:2+4712:02+4722:06+060322:1022+456++++ZAC+++...
;3.Beispiel
ZAB+GHI:3+4713:03+4723:07+060323:1023+789++++ZAC+++...
; Ende testdatei
---------
gesplittete Zeile:
ZAB+GHI:3+4713:03+4723:07+060323:1023+789++++ZAC+++...
Thisline alle "++" durch "+$+"ersetzt [ZAB+GHI:3+4713:03+4723:07+060323:1023+789+$+$+$+]  
ZAB_1A=GHI
ZAB_1B=3
ZAB_2A=4713
ZAB_2B=03
ZAB_3A=060323
ZAB_3B=1023
ZAB_4A=060323
ZAB_4B=1023
ZAB_5A=789

Gruß
Biber
Member: many
many Apr 17, 2006 at 02:49:54 (UTC)
Goto Top
Moin Biber,

zunächst einmal vorneweg ein großes Dankeschön, dass Du Dich so in mein Substring-Problem reingekniet hast.
Bei dem Versuch, Dein Batch-Script auch im Details zu begreifen, bin ich doch auf die ein oder andere Denksportaufgabe für mich gestoßen. Aber Deine Kommentare und Debugzeilen haben geholfen.
Im Einzelnen:
- Zeile 04: ZAB_4A statt ZAB_3A ?
- Zeile 06: (%neededvars%) statt ( "%needevars%") ? Okay, so initialisiert man also Variablen in einer Schleife.
- Zeile 12 + 14: Dass es evt. einen ZAA-Block geben könnte, läßt Du außer acht. Ich schlage daher für die Funktion die Einfügung vor:
:: --- optionalen ZAA-Block (am Anfang) abschneiden ---
Set thisline=%thisline:ZAB=+{%
For /f "tokens=2 delims={" %%i in ("%thisline%") do set "thisline=%%i"
:: echo.%thisline%
- Die zu analysierende Zeile ist immer die erste Zeile. Wie stelle ich das denn ein?
- Zeile 20: Das gefällt mir gut (:ZAC=}), so einfach erhalte ich ein einstelliges Trennzeichen.
- Vor der doppelt geschachtelten For-Schleife am Ende mit doppeltem If kapituliere ich noch, aber es funktioniert tadellos (mit tokens=1-8 statt tokens=2-9).

Ich bedanke mich schon mal recht herzlich für die Hilfestellung. Jetzt sind wir (nein, Dir gehören die Blumen!!!) schon ganz nah dran. Ich bin positiv überrascht, dass Stringoperationen dieses Art machbar sind.

Schönen Gruß

Many
Member: Biber
Biber Apr 17, 2006 at 11:31:55 (UTC)
Goto Top
Moin Many,

freut mich, wenn es Dir weiterhilft.
Zu Deinen Anmerkungen:
Zeile 04 und 06: Ja, sind Tippfehler.
Wobei diese %neededvars%-Lösch-Mimik nur nötig ist, solange die ganzen %ZAB_xx%-Variablen wie in dem Beispiel oben GLOBAL angelegt werden.
Normalerweise, wenn sie in dem gleichen Batch weiterverarbeitet werden, wie Du es ja eigentlich vorhast, gehört hinter die erste Zeile noch ein
@echo off & setlocal
...dann sind diese Zeilen überflüssig. Im jetzigen Zustand wäre das Problem, das ZAB_xx-Variablen noch gesetzt sein könnten, wenn Du den Batch erst über eine Datei Zaatest1.txt laufen lässt und danach über eine andere Datei Zaatest2.txt.
Zeile 12 und 14... ja, da habe ich etwas mit der Interpretation der Anforderung gekämpft. Ich hatte dann beschlossen, es so zu interpretieren, dass nur "Satzart ZAB", also Sätze, die mit "ZAB" beginnen, verarbeitet werden.
Aber, gute Nachricht: alle anderen Sätze (z.B. beginnend mit ZAA) können ja mit genau Deiner "Set thisline=%thisline:ZAB=+{%"-Strategie in Sätze umgewandelt werden, die der Batch händeln kann.
Unten die "tokens=2-9" statt "tokens=1-8" kamen bei mir halt so zustande, weil bei mir das Token 1 immer dem String "ZAB" enthält.

Zur Frage: "- Die zu analysierende Zeile ist immer die erste Zeile. Wie stelle ich das denn ein?" ..tja, wie beschrieben, ich nehme nur die "Satzart ZAB" - alles, was mit "ZAB" beginnt durch den "findstr /b 'ZAB'"-Befehl.
Die anderen "Satzarten", also beginnend mit "ZAA" oder "ZAC" würde ich entspechend mit 'findstr /b "ZAA"' oder 'findstr "ZAC"'-rausfiltern ODER
mit 'findstr "ZAB"' (ohne Parameter /b) oder 'Find ZAB' alle Zeilen rausfiltern, die einen ZAB-Block enthalten.
An dieser Stelle weiß ich allerdings nicht, welche ZAB-Blöcke denn die sind, die Du verarbeiten willst bzw. welche denn Priorität vor den Anderen haben.

Aber Du hast ja erst mal den Anfang und ich denke, die Anpassung bekommst Du auch weiter hin. Im Großen und Ganzen scheint es ja in die richtige Richtung zu gehen.

Grüße
Biber
Member: many
many Apr 17, 2006 at 15:44:02 (UTC)
Goto Top
Moin Biber,

ich habe die letzten kleinen Anpassungen vorgenommen. Es läuft jetzt prima. Vielen Dank für die Unterstützung.

Schönen Gruß.

Many
Member: Biber
Biber Apr 18, 2006 at 09:55:16 (UTC)
Goto Top
Moin, Many,
ich fände es nett, wenn Du den funktionstüchtigen Batch mit Deinen Änderungen hier kurz posten könntest (oder zumindest die oben diskutierten Teile davon).
Dann haben alle Mitleser etwas davon face-wink

Ansonsten schönen Tag
Biber
Member: many
many Apr 18, 2006 at 20:04:53 (UTC)
Goto Top
Moin Biber,

hier der letzte Stand, der alle Funktionen beinhaltet, die ich benötige:

Script zabsplit.cmd:
@echo off & setlocal
Set "NeededVars=ZAB_1A ZAB_1B ZAB_2A ZAB_2B ZAB_3A ZAB_3B ZAB_4A ZAB_4B"
Set "NeededVars=%neededVars% ZAB_5A ZAB_5B ZAB_6A ZAB_6B ZAB_7A ZAB_7B ZAB_8A ZAB_8B"
FOR %%a in (%neededvars%) do set "%%a="
::Echo Dateiinhalt %1:
::Type %1
::Echo ---------
:: nur 1. Zeile verarbeiten:
for /f "delims=" %%i in ('more ^< %1') do if not defined ZAB_1A call :ZABSplit %%i
set ZAB
goto :eof
:: ----------------------------------------------------------------------------
:ZABSplit
If [%1]== goto :eof
Set thisline=%1
echo.%thisline%
:: --- optionalen ZAA-Block (am Anfang) abschneiden ---
Set thisline=%thisline:ZAB=+{%
For /f "tokens=2 delims={" %%i in ("%thisline%") do set "thisline=%%i"
echo.%thisline%
:: --- ZAC-Block (am Ende) abschneiden ---
Set thisline=%thisline:ZAC=}%
For /f "tokens=1 delims=}" %%i in ("%thisline%") do set "thisline=%%i"
echo.%thisline%
:: --- leere Felder mit Dummyzeichen füllen
Set thisline=%thisline:++=+$+%
Set thisline=%thisline:++=+$+%
echo.%thisline%
For /f "tokens=1-8 Delims=+" %%a in ("%thisline%") do (
IF [%%a] NEQ IF [%%a] NEQ [$] for /f "delims=: tokens=1-2" %%i in (":%%a") do (
Set ZAB_1A=%%i & Set ZAB_1B=%%j)
IF [%%b] NEQ IF [%%b] NEQ [$] for /f "delims=: tokens=1-2" %%i in (":%%b") do (
Set ZAB_2A=%%i & Set ZAB_2B=%%j)
IF [%%c] NEQ IF [%%c] NEQ [$] for /f "delims=: tokens=1-2" %%i in (":%%c") do (
Set ZAB_3A=%%i & Set ZAB_3B=%%j)
IF [%%d] NEQ IF [%%d] NEQ [$] for /f "delims=: tokens=1-2" %%i in (":%%d") do (
Set ZAB_4A=%%i & Set ZAB_4B=%%j)
IF [%%e] NEQ IF [%%e] NEQ [$] for /f "delims=: tokens=1-2" %%i in (":%%e") do (
Set ZAB_5A=%%i & Set ZAB_5B=%%j)
IF [%%f] NEQ IF [%%f] NEQ [$] for /f "delims=: tokens=1-2" %%i in (":%%f") do (
Set ZAB_6A=%%i & Set ZAB_6B=%%j)
IF [%%g] NEQ IF [%%g] NEQ [$] for /f "delims=: tokens=1-2" %%i in (":%%g") do (
Set ZAB_7A=%%i & Set ZAB_7B=%%j)
IF [%%h] NEQ IF [%%h] NEQ [$] for /f "delims=: tokens=1-2" %%i in (":%%h") do (
Set ZAB_8A=%%i & Set ZAB_8B=%%j)
)
echo.
goto :eof
Input-File zaatest.txt (3 Beispielzeilen, die zu testende Zeile muß jeweils Zeile 1 sein:
ZAA+001ZAB+ABC:1+4711:01+4721:05+060321:1021+123++1++ZAC+++...
ZAA+002ZAB+DEF:2+4712:02+4722:06+060322:1022+456++++ZAC+++...
ZAB+GHI:3+4713:03+4723:07+060323:1023+789++++ZAC+++...
Ausgabe:
D:\Tools\DOS\sample>zabsplit zaatest.txt
ZAA+001ZAB+ABC:1+4711:01+4721:05+060321:1021+123++1++ZAC+++...
+ABC:1+4711:01+4721:05+060321:1021+123++1++ZAC+++...
+ABC:1+4711:01+4721:05+060321:1021+123++1++
+ABC:1+4711:01+4721:05+060321:1021+123+$+1+$+

ZAB_1A=ABC
ZAB_1B=1
ZAB_2A=4711
ZAB_2B=01
ZAB_3A=4721
ZAB_3B=05
ZAB_4A=060321
ZAB_4B=1021
ZAB_5A=123
ZAB_7A=1

D:\Tools\DOS\sample>
In die Struktur der Input-Datei muß man sich etwas einlesen (s.o.), aber selbst diese recht eigenwillige Struktur kann ausgewertet werden. Vielen Dank für die Unterstützung.

Schönen Gruß

Many
Member: Biber
Biber Apr 18, 2006 at 20:07:01 (UTC)
Goto Top
Danke auch Dir face-wink

Grüße
Biber