andynix
Goto Top

Text von festen Positionen auslesen und in 2te Datei schreiben

Hallo zusammen,
ich brauche etwas mehr Hilfe um ein kleines Problem zu lösen.

Ich bekomme Textdateien die an bestimmten Positionen die benötigten Infos beinhalten.
Es sind immer feste Positionen und feste Anzahl Zeichen.
Daraus wird ein SQL Script generiert der dann in die Datenbank gepumpt wird.
Bis jetzt werden die Dateien händisch auf ein AutoitScript geschmissen der dann das SQL Script generiert.

SQL Script sieht so aus
INSERT INTO [SchnittstelleLog].[dbo].[Auswertung] ([Aktionszeit], [Versendungsart]) VALUES('20090915144539', 'P');
INSERT INTO [SchnittstelleLog].[dbo].[Auswertung] ([Aktionszeit], [Versendungsart]) VALUES('20090915143523', 'P');

Die Textfiles sehen ungefähr so aus. (am ende Rauten #, aber sonst keine Sonderzeichen, nur ab und zu Leerzeichen)
ASDF000123213213 0025487878987

Benötigte Zeichenketten:
$Aktionszeit = 14, 4
$Versendungsart = 20, 1
$IDP = 42, 8
$IDS = 25, 6

Meine Ansatz sieht so aus...
@echo off & setlocal & enableDelayedExpansion 
Datei=C:\Import\*.txt

for /f "usebackq delims=" %%i in ("%Datei%") do call :ProcessLine "%%i"  
goto :eof
:ProcessLine
set "Zeile=%~1"  

::ab 14 Zeichen 4 Buchstaben ausgeben
(echo %Zeile:~14,4%)>>"script.sql"  

goto :eof

Muss ich für jede Zeichenkette eigene :ProcessLine erstellen ? ABER wie ?

Content-Key: 126045

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

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

Member: bastla
bastla Sep 29, 2009 at 18:00:19 (UTC)
Goto Top
Hallo Andynix!
Muss ich für jede Zeichenkette eigene :ProcessLine erstellen ?
Mitnichten - das "Line" bezieht sich auf die eingelesene Zeile.

Die Ausgabezeile musst Du aus den einzelnen Bestandteilen zusammensetzen, also etwa:
>>"script.sql" echo INSERT INTO [SchnittstelleLog].[dbo].[Auswertung] ([%Zeile:~14,4%], [%Zeile:~20,1%]) VALUES('%Zeile:~42,8%%Zeile:~25,6%','P');
Grüße
bastla
Member: Biber
Biber Sep 29, 2009 at 19:52:05 (UTC)
Goto Top
Moin Andynix und bastla,

noch zweieinhalb Ergänzungen dazu.

Wenn es sich doch um Massendatenverarbeitung handelt, dann ist der Plan, das mit einzelnen INSERT-Statements zu machen, ja schon mal ein Anfang.
Aber es gibt Datenbanken, die brauchen dann zum Einlesen von 10000 dieser Einzel-Inserts ein Stündchen, weil der Treiber leider Gates kein Abschalten des Autocommits zulässt und nach jedem f*cking Kasperstatement erstmal warten angesagt ist.

Ebenso gibt es allerdings auch -je nach Datenbankbleich- mehrere bessere und/oder Umgehungsstrategien.
Da du von SQLServer sprichst und diese "[dbo].[Tabellenname]"-Schreibweise es nahelegt, das du wohl zur täglichen Arbeit mit einen M$-Sqlserver gezwungen wirst:

Wenn Du keine Dump/DataPumpfiles erzeugen willst/sollst, sondern über INSERT-Statements gehen musst:

  • Versionen vor SqlServer 2005:
Erzeuge Sql-Skripts STATT mit dem Statement
INSERT INTO [SchnittstelleLog].[dbo].[Auswertung] ([Aktionszeit], [Versendungsart]) 
VALUES('20090915144539', 'P');  
INSERT INTO [SchnittstelleLog].[dbo].[Auswertung] ([Aktionszeit], [Versendungsart]) 
VALUES('20090915144123', 'P');  

....

... lieber STATTDESSEN solche:

INSERT INTO  [SchnittstelleLog].[dbo].[Auswertung] ([Aktionszeit], [Versendungsart]) 
SELECT '20090915144539', 'P'  
UNION ALL
SELECT '20090915144123', 'P'  
UNION ALL
SELECT '20090915144234', 'P'  
UNION ALL
SELECT '20090915144456', 'P'  
UNION ALL
SELECT '20090915144567', 'X'  
UNION ALL

.....

[...und noch 30 weitere UNION ALL...nächste Zeile-mit-Values..]
GO

[...nächste 30 Sätze]...
GO
...

Ab SqlServer 2008 geht es etwas normaler (also ohne Workaround):
INSERT INTO  [SchnittstelleLog].[dbo].[Auswertung] ([Aktionszeit], [Versendungsart]) 
VALUES 
('20090915144539', 'P'),   
('20090915144123', 'P'),   
('20090915144234', 'P'),   
('20090915144345', 'P'),   
('20090915144567', 'X'),  
....

Dann geht die Erzeugung des SQls etwas flotter, die .sql-Dateien werden handlicher und wartbarer und selbst ein MSSqlServer rennt wie Sau.

Grüße
Biber
Member: Andynix
Andynix Sep 30, 2009 at 04:44:26 (UTC)
Goto Top
Danke bastla - werde gleich umsetzen.
Danke Bieber - ja der INSERT ist recht langwierig.

Mache mich an die Arbeit und melde mich nachher wieder. (entweder hilfebietend oder um das Ergebnis anzubieten)
Member: Andynix
Andynix Sep 30, 2009 at 07:59:26 (UTC)
Goto Top
Ich glaubte "gut vom Begriff" zu sein aber ich bin doch zu blöd um:
1.) die Schleife für mehre Text Dateien zu erweitern, und
2.) eine Datei zu überspringen, wenn "IDST0000"

@echo off & setlocal enableDelayedExpansion
set "Ordner=z:\"  
set "filter=*.txt"  

:check
for  %%i in (dir /b %ordner%*.txt) do ( for /f "usebackq delims=" %%i in ("%Ordner%%filter%") do call :ProcessLine "%%i")  
goto :eof
:ProcessLine
set "Zeile=%~1"  
echo>>"script.sql" INSERT INTO [SchnittstelleLog].[dbo].[Auswertung] ([Aktionszeit], [Versendungsart], [Vorgangsnummer], [Aktenzeichen] , [IDST]) VALUES('%Zeile:~103,4%', '%Zeile:~89,1%', '%Zeile:~200,6%/%Zeile:~217,8%/%Zeile:~236,4%', '-', '%Zeile:~24,6%' );  

...gibt es noch Hilfe für mich ?
Member: bastla
bastla Sep 30, 2009 at 08:45:44 (UTC)
Goto Top
Hallo Andynix!

Etwa so:
@echo off & setlocal enableDelayedExpansion
set "Ordner=z:\"  
set "filter=*.txt"  

:check
pushd "%Ordner%"  
for  %%d in (dir /b "%filter%") do ( for /f "usebackq delims=" %%i in ("%%d") do call :ProcessLine "%%i")  
popd
goto :eof
:ProcessLine
set "Zeile=%~1"  
>>"script.sql" echo INSERT INTO [SchnittstelleLog].[dbo].[Auswertung] ([Aktionszeit], [Versendungsart], [Vorgangsnummer], [Aktenzeichen] , [IDST]) VALUES('%Zeile:~103,4%', '%Zeile:~89,1%', '%Zeile:~200,6%/%Zeile:~217,8%/%Zeile:~236,4%', '-', '%Zeile:~24,6%' );  
goto :eof
eine Datei zu überspringen, wenn "IDST0000"
"IDST0000" bezieht sich auf den Dateinamen oder den Inhalt? So oder so, Du kannst dafür "findstr /v" verwenden ...

Grüße
bastla
Member: Andynix
Andynix Sep 30, 2009 at 11:19:33 (UTC)
Goto Top
Die Datei "dir" kann nicht gefunden werden.Die Datei "/b" kann nicht gefunden werden.

...auch Hochkomma oder Anführungszeichen gehen nicht face-sad z.B. ('dir /b "%filter%"')
"IDST0000" bezieht sich auf den Inhalt.

Ich muss die "Batch for Runaways" genauer durch studieren.
Member: bastla
bastla Sep 30, 2009 at 11:34:57 (UTC)
Goto Top
Hallo Andynix!

Sorry, hatte vorher nicht genau genug gelesen ... face-sad

Natürlich sind in einer "for /f"-Schleife Hochkommata gefragt - mit dem Filtern zusammen könnte das etwa so aussehen:
@echo off & setlocal enableDelayedExpansion
set "Ordner=z:\"  
set "filter=*.txt"  

:check
pushd "%Ordner%"  
for  /f %%d in ('dir /b "%filter%'") do for /f "delims=" %%i in ('findstr /v "IDST0000" "%%d"') do call :ProcessLine "%%i"  
popd
goto :eof

:ProcessLine
set "Zeile=%~1"  
>>"script.sql" echo INSERT INTO [SchnittstelleLog].[dbo].[Auswertung] ([Aktionszeit], [Versendungsart], [Vorgangsnummer], [Aktenzeichen] , [IDST]) VALUES('%Zeile:~103,4%', '%Zeile:~89,1%', '%Zeile:~200,6%/%Zeile:~217,8%/%Zeile:~236,4%', '-', '%Zeile:~24,6%' );  
goto :eof
Falls nicht unmittelbar nach dem String "IDST0000", sondern nach "0000" an der entsprechenden Zeilenposition gefiltert werden soll, musst Du das eben im Unterprogramm ":ProcessLine" erledigen ...

Grüße
bastla

P.S.:
Ich muss die "Batch for Runaways" genauer durch studieren.
Gegen diesen Vorsatz ist nun wirklich nix einzuwenden ... face-wink

[Edit] Überzählige Klammer am Ende der "for"-Zeile entfernt [/Edit]
Member: Andynix
Andynix Sep 30, 2009 at 12:22:16 (UTC)
Goto Top
Batch for Runaways - bin gerade dabei...

Trotzdem habe ich ein Problem - die Textdateien haben Leerzeichen in Namen! - deswegen greift sich FINDSTR nur den ersten Teil des Namens.
Member: bastla
bastla Sep 30, 2009 at 12:34:36 (UTC)
Goto Top
Hallo Andynix!
die Textdateien haben Leerzeichen in Namen! - deswegen greift sich FINDSTR nur den ersten Teil des Namens
Kann eigentlich nicht sein, da "%%d" ja unter Anführungszeichen steht - eher schon, weil ein "delims=" fehlt - das wäre jetzt der Moment, für die Dateiauswahl ganz auf "for /f" zu verzichten (dann könnten auch "pushd" und "popd" entfallen) und das so zu formulieren:
for %%d in ("%ordner%%filter%") do for /f "delims=" %%i in ('findstr /v "IDST0000" "%%d"') do call :ProcessLine "%%i"
Wenn Du übrigens anstelle von "echo off" ein "echo on" verwendest, kannst Du genau nachvollziehen, was jeweils an Befehlen ausgeführt wird (bei der Gelegenheit ließe sich dann auch gleich das unnötige "setlocal enableDelayedExpansion" eliminieren).

[Edit]
Wie's aussieht ist heute ist nicht so ganz mein Tag - Du hattest ja "Datei überspringen" geschrieben, und dann müsste die Zeile eher so aussehen:
for %%d in ("%ordner%%filter%") do findstr "IDST0000" "%%d">nul || for /f "usebackq delims=" %%i in ("%%d") do call :ProcessLine "%%i"
[/Edit]

Grüße
bastla