[Powershellscript] Eine Textdatei durchsuchen nach bestimmten Zahlen und Wörtern.

Borstel86

Lt. Junior Grade
Registriert
März 2008
Beiträge
386
Guten Abend,

ich habe jetzt vor kurzem eine kleines Script geschrieben womit ich mir eine gewisse Arbeitserleichterung geschaffen habe, jetzt habe ich ein bisschen Blut geleckt und eine neue, aber auch für mich sehr schwierige Idee.

Ich würde gerne PDF Dokumente per Kommandozeile in eine Textdatei umwandeln (im Moment noch nicht wichtig) und dann aus dieser Textdatei mir ein paar Zahlen raussuchen um diese weiterzuverarbeiten. Jetzt habe ich schon ein bisschen gesucht und mehrere Sachen gefunden, select-string oder get-content. Ich weiß bloß nicht wie ich sie anwende.

Im Endeffekt ist die Textdatei nach folgenedem Schema aufgebaut.

Zahl1: xxx Zahl2: xxx Zahl3: xxx

Jetzt hätte ich gerne nur jeweils die "xxx" als jeweils einzelne Variable.

Vielleicht könnt ih mir ein bisschen helfen, danke sehr.

MfG
Borstel
 
Machs dir einfach und schau, dass du die Daten als CSV oder XML reinkriegst, benutz dann import-csv oder dergleichen und durchsuch die Variable mit einzelnen Arrayelementen auf die du direkt zugreifen kannst ohne mit RegEx hantieren zu müssen.

//e: ach warte, du machst da ja mit die Trennung.

Da ist dann wohl doch RegEx das richtige. Du brauchst einen Ausdruck, der dir das Schema eines Datensatz erkennt, und den schreibst du dir dann raus. Kann ich dir grad aufm Telefon nicht raussuchen, müsste sich aber was zu finden lassen. www.regex101.com hilft.

Das schwierigste wird sein, zuverlässige Daten aus dem PDF zu kriegen. Sind die gescannt, oder direkt vom PC erstellt?
 
Zuletzt bearbeitet:
Die PDF ist elektronisch erstellt, ich würde es ja erstmal mit einer provisorisch geschriebenen Textdatei versuchen. Nur um zu testen ob es machbar wäre.
 
Die schnellste und flexibelste Lösung wäre wohl den .net Filestream reader zu nehmen und zeilenweise per Regex nach dem zu suchen was du brauchst.
 
So,

also die Umwandlung der PDF funktioniert soweit ohne Probleme. Jetzt bin ich auch schon ein paar Codeschnipsel im Netz gestossen und habe mit den ein bisschen was versucht. Dies klappt erstmal ganz gut, ABER ich sehe nicht zu 100% durch was dort passiert mit dem Befehl.

Dadurch kriege ich die nächste Stufe meines Vorhabens leider nicht hin. Ich würde jetzt nicht nur gerne die Zahlen haben, sondern auch noch einen Namen und Adresse. Anscheinend ignoriert mein Select-String alle Buchstaben.

Hier mal mein Code:
Code:
$Inhalt = Get-Content "da.txt"

$Name = $Inhalt | Select-String "(?<=Name:\s)\d+" -AllMatches | foreach{$_.Matches} | foreach{$_.Value}
$Adresse = $Inhalt | Select-String "(?<=Adresse:\s)\d+" -AllMatches | foreach{$_.Matches} | foreach{$_.Value}
$Zahl1 = $Inhalt | Select-String "(?<=Zahl1:\s)\d+" -AllMatches | foreach{$_.Matches} | foreach{$_.Value}
$Zahl2 = $Inhalt | Select-String "(?<=Zahl2:\s)\d+" -AllMatches | foreach{$_.Matches} | foreach{$_.Value}
$Zahl3 = $Inhalt | Select-String "(?<=Zahl3:\s)\d+" -AllMatches | foreach{$_.Matches} | foreach{$_.Value}
Write-Output "$Name $Adresse $Zahl1 $Zahl2 $Zahl3"

Die Textdatei ist simpel:

Name: Müller, Lehmann
Adresse: 00000, Entenhausen, Straße 10
Zahl1: 001, Zahl2: 002, Zahl3: 003, Zahl.....

Meine Ausgabe ist aber leider "00000 001 002 003"

Also der Name ist gar nicht zu sehen und die Adresse auch nur die PLZ (00000).

Ich hoffe ihr habt da ein paar Tipps bzw. Erklärungen für mich. :)
 
Wie groß sind die Dateien?

Kannst du eine komplette Beispieldatei mal anhängen?


Wie gesagt, du wirst mit get-content bei großen Files Probleme bekommen. und allgemein finde ich da die .net regex operatoren und Filereader deutlich besser, wird aber sicher mit nativem Powershell auch.
 
Eine orginale Textdatei würde ich ungern anhängen, weil wie schon zu sehen, auch Adressen und Namen enthalten sind. Die Zahlen haben ja sonst auch ihre Bedeutung (Telefon etc.).

Die Textdatei ist maximal 200 Zeilen groß bzw. 3KB. Also wirklich realtiv klein.
 
du kannst ja eine anonymisierte Beispieldatei aufsetzen.

Frage:

- ist das Schema immer gleich, d.h. das erst zwei irrelevante Zeilen komme und dann die entsprechende Zeile?
- sind alle Zahlen immer in einer Zeile (Durch komme getrennt?)
- du schreibst, die Datei hat bis zu 200 Zeilen, aber die Beispieldaten sind durch Komma getrennt
- sind die immer fortlaufend nummeriert?
 
Ich habe jetzt nochmal eine gekürzte Version der Textdatei hochgeladen. Also der Abstand der Zeilen kann sich ab und zu verändern, deswegen will ich es ja nicht an den Zeilen festmachen, sondern an den Schlagwörtern.

Die Zeile mit den Zahl1.... ist immer gleich aufgebaut und variiert eigentlich nur bei den Zahlen selber.

Mit dem select-string sieht das finde ich schon ganz gut aus, habe nun auch rausgefunden dass es am Operator \d+ liegt, dieser sucht wohl nur nach Zahlen. Am besten wäre halt Zahlen und Wörter.

MfG
 

Anhänge

  • da.txt
    351 Bytes · Aufrufe: 235
Zuletzt bearbeitet:
Ergebnis siehe Anhang

Code:
$ExportFile = 'dein file'

$reader = New-Object System.IO.StreamReader ((Get-Item $ExportFile),[System.Text.Encoding]::GetEncoding('UTF-8')) #ggf die Kodierung anpassen

$regex_pattern = '([Z][a][h][l][0-9]+[:][ ]+)([0-9]+)'


 try {
    for() 
            {
            $line = $reader.ReadLine()
            $match = $null
            if ($line -eq $null) { break }
            # process the line

            #prüfe auf Pattern
            $match = [regex]::Matches($line,$regex_pattern)
    

            $match | foreach { echo $_.Groups[2].value }
            

            #echo $line


    }
  
}
finally {
    $reader.Close()
}
Unbenannt.png
 
Das sieht ja schonmal super aus, in die Richtung bin ich ja mit dem Select-String auch gekommen. Also ich konnte die eigentlichen Zahlen hinter "Zahl1:" zum Beispiel auslesen. Nur wenn ich es jetzt noch um den Kundennamen und Anschrift erweitern will, da wird es schwierig.

Wenn ich das richtig erkannt habe, gibt man mit dem regex_pattern vor wie das Muster aussieht. Also würde sowas hier für die Adresse funktionieren?

Code:
$regex_pattern = '([A][d][r][e][s][s][e]+[:][ ]+)([A-Z]+)'

Oder ist das Quatsch bzw. Sinnlos? Finde die Lösung für die Adresse auf den ersten Blick nicht sehr "schön". :D
 
Adresse wäre

$regex_pattern2 = '(^[A][d][r][e][e][:][ ]*)((.)*)'

und dann wäre die Adresse in $2 drinnen

Anmerkung: Regex Pattern hängen sehr stark davon ab wie genau die Datei aufgebaut und wie "streng" der aufbau ist, daher ist es immer schwierig ohne alle Daten zu kennen genaue Aussagen zu treffen. z.B. gehe ich oben davona su, dass die Zeile immer mit Adresse anfängt und keine Leerzeichen vorran stehen.
 
Zuletzt bearbeitet:
Sieht ja auch ganz gut aus. Die Datei ist an sich definitiv immer gleich vom Grundgerüst. Deine Varianten prüfe ich dann mal wenn ich zu hause bin. Die Frage die mir sich jedoch stellt, wie kann ich jetzt die gefunden Zahlen und Adresse mit jeweils eigene Variable benutzen. Also das jede Zahl und auch die Adresse weiter zu verwenden sind, zum erstellen eines Ordners etc.
 
allgemein gesprochen hast du innerhalb der foreach Schleife darauf zugriff bzw bei der Adresse innerhalb des regex match (und könnte ein mkdir absetzen)

....

oder du liest dir alles in ein array ein und loopst das nochmla durch

...

oder du liest dir alles in ein Hash Table ein und erstellst darauf am ende deine Objekte

...

gibt mehrere Möglichkeiten, aber das kann recht schnell komplex werden und du hast dann vermutlich auch das Problem, dass du die Adressen vorher normieren musst, um sicherzustellen dass du daraus dateien erstellen kannst. Das gleiche gilt für Zahlen, wenn nicht sichergestellt ist dass die Zahl noch nciht vorher vorkam.

Vom Aufwand her kann sowas aber recht schnell das sprengen, was man in einem Forum kostenlos als Support leisten kann und wenn du es das erste mal machst, wirst du auch deutlich länger brauchen
 
Wie du dass so sagst, könnte man ja Angst kriegen. 😄

Gibt es denn keine Möglichkeit an meiner select-String Methode den richtigen Operator zu finden? Immerhin kriege ich da schon meine Zahlen als Variable raus. Nur an Wörtern wie Name und Adresse scheitert es, weil ich nicht auf den Trichter komme welcher Operator da richtig wäre. Mit dem \d+ kommen wohl nur zahlen.
 
Zuletzt bearbeitet:
Geht vermutlich ; aber wird deutlich langsamer als meine Variante sein und im Ergebnis hast du es auch nur in einer variablen (genau wie in meiner Variante). Was du daraus machst ist ja nochmal eine andere frage

Also klar ; man kann es vermutlich umbauen; aber mit Hinblick auf dein Problem wird man ja nichts gewinnem oder was erhoffst du dir davon !?
 
So, ich habe nochmal rumprobiert. Habe jetzt erstmal für mich ein Ergebnis mit dem ich durchaus zufrieden bin. Ich habe jetzt erstmal alles einzeln und als Variable zu verfügung. Es ist sicherlich für Profis ein total schrecklicher Code, aber er funktioniert. :D

Code:
    $Inhalt = Get-Content "da.txt"

     
    $Kundetemp = $Inhalt | Select-String "(?<=Kunde:\s)\w" -AllMatches 
    $Adressetemp = $Inhalt | Select-String "(?<=Adresse:\s)\w" -AllMatches 
    $Zahl1 = $Inhalt | Select-String "(?<=Zahl1:\s)\d+" -AllMatches | foreach{$_.Matches} | foreach{$_.Value}
    $Zahl2 = $Inhalt | Select-String "(?<=Zahl2:\s)\d+" -AllMatches | foreach{$_.Matches} | foreach{$_.Value}
    $Zahl3 = $Inhalt | Select-String "(?<=Zahl3:\s)\d+" -AllMatches | foreach{$_.Matches} | foreach{$_.Value}
    $Kundesplit = "$Kundetemp".split(":")
    $Kunde = $Kundesplit[1]
    $Adressesplit = "$Adressetemp".split(":")
    $Adresse = $Adressesplit[1]
    
    Write-Output "Herr/Frau $Kunde"
    Write-Output "Wohnt in $Adresse"
    Write-Output "$Zahl1"
    Write-Output "$Zahl2"
    Write-Output "$Zahl3"

Damit erhalte ich dann:

Herr/Frau Donald, Duck
Wohnt in 01234 Entenhause, Straße der Gans 10
0001
000002
003

Das einzige was mich etwas stört, er übernimmt vor dem Namen und der Straße noch das Leerzeichen. :/

Edit: Was die Schnelligkeit des Scripts angeht, die ist eigentlich realtiv zu vernachlässigen. Ich denke mal alles was unter 30s Verarbeitungsdauer bleibt, ist mehr als ausreichend. Ich will ja per Klick immer nur eine PDF/TXT Datei verarbeiten und das geht ja realtiv schnell bei den paar Daten.
 
Zuletzt bearbeitet:
Das stimmt, habe es beim testen nicht mit reingeschrieben. Weil die Sachen mit Zahl1, Zahl2... eh dann durch die echten Bezeichnungen ersetzt wird. Ich war also einfach nur faul und habe Zahl4 nicht mit reingecodet. :D
 
Borstel86 schrieb:
So, ich habe nochmal rumprobiert. Habe jetzt erstmal für mich ein Ergebnis mit dem ich durchaus zufrieden bin. Ich habe jetzt erstmal alles einzeln und als Variable zu verfügung. Es ist sicherlich für Profis ein total schrecklicher Code, aber er funktioniert. :D

Code:
    $Inhalt = Get-Content "da.txt"

     
    $Kundetemp = $Inhalt | Select-String "(?<=Kunde:\s)\w" -AllMatches 
    $Adressetemp = $Inhalt | Select-String "(?<=Adresse:\s)\w" -AllMatches 
    $Zahl1 = $Inhalt | Select-String "(?<=Zahl1:\s)\d+" -AllMatches | foreach{$_.Matches} | foreach{$_.Value}
    $Zahl2 = $Inhalt | Select-String "(?<=Zahl2:\s)\d+" -AllMatches | foreach{$_.Matches} | foreach{$_.Value}
    $Zahl3 = $Inhalt | Select-String "(?<=Zahl3:\s)\d+" -AllMatches | foreach{$_.Matches} | foreach{$_.Value}
    $Kundesplit = "$Kundetemp".split(":")
    $Kunde = $Kundesplit[1]
    $Adressesplit = "$Adressetemp".split(":")
    $Adresse = $Adressesplit[1]
    
    Write-Output "Herr/Frau $Kunde"
    Write-Output "Wohnt in $Adresse"
    Write-Output "$Zahl1"
    Write-Output "$Zahl2"
    Write-Output "$Zahl3"

Damit erhalte ich dann:



Das einzige was mich etwas stört, er übernimmt vor dem Namen und der Straße noch das Leerzeichen. :/

Edit: Was die Schnelligkeit des Scripts angeht, die ist eigentlich realtiv zu vernachlässigen. Ich denke mal alles was unter 30s Verarbeitungsdauer bleibt, ist mehr als ausreichend. Ich will ja per Klick immer nur eine PDF/TXT Datei verarbeiten und das geht ja realtiv schnell bei den paar Daten.

Ich frag mich wie du da das gewünschte Ergebnis bekommen kannst.
Ich bekomme:
Code:
Herr/Frau  XX

Nummer des Auftrags
Wohnt in  XX

Nummer des Auftrags
0001
000002
003
(?<=Kunde:\s)\w könnte auch niemals das ß (scharfes s) in Straße match'en, da \w nur [A-Za-z0-9_] findet. Deutsche Umlaute/Sonderzeichen sind hier nicht enthalten.


Versuchs mal damit:
Code:
$Inhalt = Get-Content "da.txt"

$Kunde = [regex]::Match($Inhalt , '(?<=Kunde:\s).+').Value 
$Adresse = [regex]::Match($Inhalt , '(?<=Adresse:\s).+').Value 
$Zahl1 = [regex]::Match($Inhalt , '(?<=Zahl1:\s)\d+').Value 
$Zahl2 = [regex]::Match($Inhalt , '(?<=Zahl2:\s)\d+').Value 
$Zahl3 = [regex]::Match($Inhalt , '(?<=Zahl3:\s)\d+').Value 


Write-Output "Herr/Frau $Kunde" 
Write-Output "Wohnt in $Adresse" 
Write-Output "$Zahl1" 
Write-Output "$Zahl2" 
Write-Output "$Zahl3"

Ergebnis:
Code:
Herr/Frau Donald, Duck
Wohnt in 01234 Entenhause, Straße der Gans 10
0001
000002
003

Matches (Mehrzahl) ist eh nicht notwendig, da nur einmal gematch'ed werden kann/soll.
 
Zuletzt bearbeitet:
Zurück
Oben