Funktionierender regulärer Ausdruck (CMD) um aus einer Textdatei bestimmte Zeilen mit " zu entfernen?

4 Antworten

Hallo Zortah,

Du könntest den Inhalt der Textdatei kopieren und in ein Microsoft Excel Sheet Einfügen "als Werte".

Dann hast Du alle Zeilen in Excel.

Da kannst Du dann oben den Filter (Daten - Filter) aktivieren und dann im Filter nach " suchen. Also im Filtersuchfeld " eingeben.

Dann findet er z.B. 500 von 3000 Zeilen, und die löschst Du alle.

Danach alles wieder kopieren in eine neue Textdatei.


GandalfAwA  20.06.2024, 17:12

PS: Ich sehe gerade, Du willst die Zeilen ohne " am Anfang löschen.

Hier kannst Du es umgekehrt machen, also erst Filtern alle Zeilen die mit " anfangen, und die markiert Du in Grün und danach filterst Du nach allen Zeilen die nicht Grün sind, und die löschst Du.

0
Zortah123 
Beitragsersteller
 20.06.2024, 17:15

Excel oder irgendwas manuell kopieren ist genauso wie der Ansatz mit Powershell keine Option, da es hier um sehr große und sehr zahlreiche Dateien geht und das Hauptanliegen für meine Frage in erster Linie der Faktor Zeit ist. Es wäre also sehr gut wenn es einen funktionierenden Ansatz per Konsole (Grep oder Ähnl) gibt.

1
GandalfAwA  20.06.2024, 17:31
@Zortah123

Achso, verstehe ... wenn Du keine Lösung findest, ich fürchte, da wird jemand ein Skript schreiben müssen, vielleicht mit Python.

Du könntest bei Upwork ein Jobpost machen, da findest sich bestimmt ein Freelancer der für einen kleinen Preis das für Dich macht.

0
Zortah123 
Beitragsersteller
 20.06.2024, 18:34
@GandalfAwA

Sowas wie ein Script was das können sollte hab ich bereits. Ich benötige nur den korrekt escapeten? regulären Ausdruck für " und die Anweisung am Anfang einer Zeile zu suchen. Normalerweise sollte das nicht mehr als 1 Zeile Code sein... Dafür jetzt extra tagelang einen Freelancer suchen oder Geld ausgeben?

0

Wieso hast Du grep, wenn Du die Sache mit Bordmitteln lösen?

In Batch gibts dafür findstr .

Allerdings spielt Batch/Cmd bezüglich DoubleQuotes (") nach eigenen Regeln. SingleQuotes(') werden als ganz normale Textzeichen verwendet und haben keinerlei Steuerfunktionen (außer bei For/f-backq"-loops, aber das ist eine Story für sich) ergo kan Dein grep nicht funktionieren. Strings mit Spaces und Sonderzeichen sind mit Doublequotes als solche zu maskieren. "DoubleQuotes" innerhalb eines durch Doublequotes maskierten Strings sind durch verdoppeln speziell zu maskieren (in einigen Ausnahmefällen kann auch durch einen Backslash\ maskiert werden, was aber vom jeweiligen aufgerufenen Programm abhängt und bei einer ungeraden Anzahl von Doublequotes den cmd-Parser aus dem Tritt bringen kann)

Beispiele:

geht schief:
Befehl "blub " irgendwas" 
so  wäre  es  richtig:
Befehl "blub "" irgendwas"

Damit findstr keinen DateiInformationen am Anfang jeder Zeile ausgibt lesen wir die Datei mit type und pipen die gelesenen Zeilen an findstr. Die cmd-Befehlszeile für das von Dir gewünschte/beschriebene Verhalten sähe dann so aus:

zeigt die Zeilen zum anschauen im Fenster an:

type "C:\Dein\pfad\test.txt"|findstr /vrc:"^"".*"

Erklärung Parameter:

  • /vrc: v=Zeieln ausschleißen , r=Regex verwenden, c=literalerString (leer- und sonderzeichen sind nur text)
  • "^"".*" ^zeilenanfang ""ein Doublequote .* kein oder mehr beliebige zeichen

Damit die guten Zeilen wieder in eine Datei geschrieben werden leitest Du die Ausgabe der oben ermittelten Zeilen einfach in eine Neue Datei um (kannst Du später umbenennen).

type "C:\Dein\pfad\test.txt" |findstr /vrc:"^"".*" >"neue Datei.txt"

Das war' s im einfachste Fall... und wieder ein Pferdefuß von cmd und findstr :wenn es schlecht läuft enthält sind gefundenen die Zeilen sind länger als 1023 Zeichen , dann meutert findstr und es hilft nur Powershell als bordeigenes Werkzeug:

in Powershell gibt es Mehrere Möglichkeiten:

erstmal nur als Text zum anschauen ins Fenster ausgeben:

(Select-String -Path 'test.txt' -Pattern '^"' -NotMatch) -replace '^.+:\d+:',''  #nicht wirklich elegant aber schnell


Get-Content -Path 'test.txt' -Encoding UTF8 |Select-String  -Pattern '^"' -NotMatch   #schon besser


Get-Content -Path 'test.txt' -Encoding UTF8 |Where-Object {$_ -NotMatch  '^".*'}   #kommt  ohne  das wuchtige  Select-Sering  zurecht (eigentlich  mein Favorit)

jetzt musst du die gefundenen Zeilen nur wieder in eine Datei bekommen.

das geht wie bei cmd mit einer Ausgabeumleitung in eine Zieldatei >''Neue_Datei.txt' :

Get-Content -Path 'test.txt' -Encoding UTF8 |Where-Object {$_ -NotMatch  '^".*'} >'Neue_Datei.txt' 

...oder mit Set-Content :

Get-Content -Path 'test.txt' -Encoding UTF8 |Where-Object {$_ -NotMatch '^".*'} |Set-Content -Path 'Neue_Datei.txt'

und wenn Du ganz schreibfaul bist gibts auch noch die Kurzform :

gc 'test.txt' -enc UTF8 |?{$_ -NotMatch '^".*'} |sc 'neu 3.txt'

ich würde Dir in jedem Fall die Verwendung von Powershell empfehlen, da es nicht die Einschränkunen von cmd und Findstr hat und RegEx volle Konformität zu .Net hat.

Woher ich das weiß:eigene Erfahrung – Ich mach das seit 30 Jahren

Zortah123 
Beitragsersteller
 20.06.2024, 22:52

Also eine Lösung mit Findstr hatte ich auch schon probiert, erzeugt aber massig Fehler, v.a bei Zeilen, die verschiedene Sonderzeichen enthalten und im Vgl zu Grep halt irre langsam. Powershell mit Get & Set-Content funktioniert und sieht auch fast so aus wie die Lösung, die ich schon umgesetzt hatte. Das Problem mit Powershell ist halt, dass auch das zu langsam läuft (Schreibrate bei mir mit ca. 1-2MB pro Sek. + stark stromfressend) Siehst du eine Lösung wie man das beschleunigen könnte? Ich müsste es auf etwa 10MB, noch besser 20MB pro Sek bringen, damit es von der Zeit her hinhaut. Grep z.b bekommt ja solche Verarbeitungsraten locker hin....

0
Erzesel  21.06.2024, 03:27
@Zortah123

Autsch.

Solch riesige Datenengen fallen im allgemeinen Gebrauch von Scripten nicht an, so das sich kaum jemand um Speed kümmert.

Also habe ich mal eine Datei mit 5Mio Zeilen (47MB) generiert, wovon 1Mio Zeilen mi einem " am Anfang markiert waren.

gc 'C:\test\input.txt' -enc UTF8 |?{$_ -NotMatch '^".*'} |sc 'neu.txt'}

benötigt auf meinem Rechner (@4,2GHz Singlethreaded) 147,6878904 Sekunden (ca. 3MB/s)

Ich habe alle Flasschenhälse durch konsequente Verwendung von puren .Net-Methoden eliminiert:

(das war schon mal eine Verdopplung der Geschwindigkeit)

  • RegEx via System.Text.RegularExpressions
  • Statt zeilenweise mit der Matches-Methode jeden einzelnen Treffer herauszufiltern , ersetze ich einfach via replace-Methode alle Zeilen mit " am Anfang bis inklusive Zeilenvorschub (\r?\n) durch einen Leerstring ''
$SR = New-Object System.IO.StreamReader("c:\test\input.txt")
$SW = New-Object System.IO.StreamWriter("c:\test\neu.txt",$false)

$Content = ($SR.ReadToEnd())
$SR.Close()
$MyFilteredText = ([regex]::Replace($content, '^".*(\r?\n)*','', 'Multiline,IgnoreCase')) 
$SW.Write([String]$MyFilteredText)
$SW.Close()

zugegeben ich war selbst überrascht:

2,3523737 Sekunden (ca. 20MB/s) , das ist 65mal schneller als zuvor und nur ein Paar Millisekunden langsamer als C#. Die Operation noch auf mehre Threads zu verteilen bringt unter einer Dateigröße von schätzungsweise 200 MB keine Vorteile, da das Einrichten und "Einsammeln" der Threads (Powershell Runspaces) auch Zeit ca 100..200msec/Runspace benötigt

Wesentlich schneller geht es nur noch in C++

0

Hast du ggf. Beispiele, wo es nicht funktioniert? Ich habe keinerlei Probleme die Zeilen zu finden, die ein " am Anfang haben oder eben welche, die das nicht haben. Sprich deine zweite Lösung mit escapten " tut bei mir schon. Oder eben etwas ala ^[^\"] um die Sachen zu finden, die kein " am Anfang haben.

Woher ich das weiß:Berufserfahrung – Softwareentwickler/Projektleiter seit 2012

Zortah123 
Beitragsersteller
 20.06.2024, 22:40

Also bei mir führt die Verwendung von grep '^\"' (2te Lösung?) dazu, dass mit "C:\input.csv" >> "C:\ordner\output.csv" keine Ausgabedatei (mehr) erzeugt wird, sondern der Text nur unendlich in der Konsole runterläuft. grep '^"' hingegen z.b erzeugt die Datei korrekt gibt aber alle Zeilen aus, die irgendwo in der Zeile ein " enthalten, nicht nur am Anfang.

0
apachy  21.06.2024, 10:48
@Zortah123

Bei mir funktionieren beide Lösungen. Getestet auf einem Windows 10 Pro System innerhalb der Git Bash mintty 3.7.0 (MINGW64).

Ggf. liegt es an den Daten. Ist sichergestellt, dass es echt einfache Double Quotes sind? Die gibt es jeweils noch in Schräglage nach links und rechts und natürlich können zwei Single Quotes je nach Schriftart auch aussehen wie Double Quotes.

Ansonsten wäre interessant was du in Windows verwendest, um grep zu nutzen. mintty? Cygwin? Was ganz anderes?

Ebenso wäre interessant, ob es an den Daten liegt, sprich kannst du uns hier ein kleines Beispiel geben, was nicht funktioniert? Hast du das Problem nur mit explizit deiner Datei oder auch, wenn du es einfach mit etwas kleinem probierst wie:

"TEST";"TEST"
"TEST";"TEST
"TEST";TEST"
"TEST";TEST
"TEST;"TEST"
"TEST;"TEST
"TEST;TEST"
"TEST;TEST
TEST";"TEST"
TEST";"TEST
TEST";TEST"
TEST";TEST
TEST;"TEST"
TEST;"TEST
TEST;TEST"
TEST;TEST

Falls es nicht daran liegt, dass z.B. irgendein nicht Standardzeichen den Kram unterbricht und zerlegt.

Oder kurz, gib uns ein konkretes Beispiel inkl. der Art, wie du es probierst, damit man es nachstellen kann.

0

Grep funktioniert prima. Der RE hier besteht aus nur zwei Zeichen (^ und "). Das Problem besteht darin, diese Zeichen in CMD einzugeben. Die Regeln dazu sind gruselig. Sobald doppelte Anführungszeichen ins Spiel kommen, dupliziere ich sie und schließe das Ganze in doppelte Anführungszeichen ein. So sollte es also gehen:

grep "^""" ...

Wenn's hart auf hart kommt, kann man den RE auch in eine Datei schreiben und mit grep -f verwenden.

Tipp: Mit sed kannst Du direkt die Originaldateien ändern:

sed -i "/^""/!p" filename(s)

löscht in CMD alle Zeilen, die nicht mit " beginnen, aus jeder Eingabedatei. Auch hier kann man komplizierte Kommandos in eine Datei schreiben und mit -f verwenden.


Zortah123 
Beitragsersteller
 21.06.2024, 18:11

grep "^""" scheint die Lösung zu sein (vielen Dank), naja das man am Ende 3 """ hinter dem "^ benötigt, muss man auch erst mal drauf kommen!

0