Forum: PC-Programmierung regex asc file parsen


von Christoph M. (mchris)


Lesenswert?

Ich würde gerne mit Hilfe regulärer Ausdrücke

https://regex101.com/
https://quickref.me/regex

ein ASC-File parsen. Das File sieht so aus
1
SYMBOL voltage -128 160 R0
2
WINDOW 123 0 0 Left 2
3
WINDOW 39 0 0 Left 2
4
SYMATTR InstName V1
5
SYMATTR Value SINE(0 1 1000)
6
TEXT -160 408 Left 2 !.tran 5m
7
TEXT 296 272 Left 2 ;time
8
TEXT 688 256 VLeft 2 ;amplitude (vertical)
9
TEXT 216 16 Left 2 ;LtSpice Labels
10
TEXT 160 352 Left 2 ;Label  size 1.5
11
TEXT 160 408 Left 3 ;Label size 2.0
12
TEXT 160 480 Left 4 ;Label size 3.5
13
TEXT 152 544 Left 4 ;Label size 3.5
14
RECTANGLE Normal 880 784 80 -32 2

und ich will die Zeilen die mit "TEXT" beginnen und ein ";" haben 
finden.

Weis jemand, wie das geht?

Der erste Vorschlag wäre
1
^.*TEXT.*$

Dieser Ausdruck holt aber alle Zeilen, die "TEXT" enthalten, auch die 
ohne ";"

von Wilhelm M. (wimalopaan)


Lesenswert?

'^TEXT.*;'

von Rolf M. (rmagnus)


Lesenswert?

Christoph M. schrieb:
> Dieser Ausdruck holt aber alle Zeilen, die "TEXT" enthalten, auch die
> ohne ";"

In dem Ausdruck kommt ja auch kein ";" vor.

Wilhelm M. schrieb:
> '^TEXT.*;'

Das matcht aber nicht auf die ganze Zeile, sondern nur auf den Teil bis 
zum Semikolon (einschließlich diesem).

Das hier matcht auf ganze Zeilen, die mit "TEXT" beginnen und irgendwo 
danach ein Semikolon enthalten:
1
^TEXT.*;.*$
Mit Capture-Groups könnte man noch die Bestandteile einzeln extrahieren.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Rolf M. schrieb:
> Das matcht aber nicht auf die ganze Zeile, sondern nur auf den Teil bis
> zum Semikolon (einschließlich diesem).

... und die gängigen Tools wie grep, awd, ... geben die ganze Zeile aus, 
wenn ein regex einem Teil der Zeile entspricht. Alles andere vergrößert 
nur die Laufzeit und ist dann eben unnötig.

von Christoph M. (mchris)


Lesenswert?

Rolf M. (rmagnus)

>Das hier matcht auf ganze Zeilen, die mit "TEXT" beginnen und irgendwo
>danach ein Semikolon enthalten:

>^TEXT.*;.*$

>Mit Capture-Groups könnte man noch die Bestandteile einzeln extrahieren.

Danke für den Code. Eigentlich brauche ich von der Zeile nur den Teil 
nach dem Semikolon. Gibt es da eine Möglichkeit, den Regex-Ausdruck zu 
kapseln und dann den Teil nach dem Semikolon zu extrahieren?

von Norbert (Gast)


Lesenswert?

1
sed -nre 's/^TEXT.*;(.*)$/\1/p' INFILE

time
amplitude (vertical)
LtSpice Labels
Label  size 1.5
Label size 2.0
Label size 3.5
Label size 3.5

von Dirk B. (dirkb2)


Lesenswert?

Christoph M. schrieb:
> Eigentlich brauche ich von der Zeile nur den Teil
> nach dem Semikolon. Gibt es da eine Möglichkeit, den Regex-Ausdruck zu
> kapseln

Dafür sind die () da

> und dann den Teil nach dem Semikolon zu extrahieren?

Das macht das \1 - der erste gesuchte Ausdruck

von Christoph M. (mchris)


Lesenswert?

Danke an alle :-)

von Christoph M. (mchris)


Lesenswert?

Das hier alleine scheint auch zu funktionieren:
1
TEXT.*;(.*)

https://regex101.com/

von Norbert (Gast)


Lesenswert?

Christoph M. schrieb:
> Das hier alleine scheint auch zu funktionieren:TEXT.*;(.*)

Scheint…

Christoph M. schrieb:
> Zeilen die mit "TEXT" beginnen

›Beginnen‹ scheint mir hier das wichtige Wort zu sein!  ;-)

von Rolf M. (rmagnus)


Lesenswert?

Christoph M. schrieb:
> Das hier alleine scheint auch zu funktionieren:TEXT.*;(.*)

In dem Fall matchen aber auch Zeilen, wo TEXT nicht am Anfang, sondern 
irgendwo in der Zeile steht, solange irgendwann danach noch ein 
Semikolon kommt.

von 🐧 DPA 🐧 (Gast)


Lesenswert?

Ich würde da gar keine regex verwenden. Einfach passend splitten:
1
#!/bin/bash
2
while IFS=';' read sargs last
3
do
4
  set -o noglob
5
  args=( $sargs )
6
  set +o noglob
7
  if [ -n "$last" ]; then args+=("$last"); fi
8
  echo "Command: ${args[0]}"
9
  echo "Arguments:"
10
  printf " - %s\n" "${args[@]:1}"
11
done

von Norbert (Gast)


Lesenswert?

Vieles was ›auch‹ möglich ist, ist nicht unbedingt auch eleganter…

von TotoMitHarry (Gast)


Lesenswert?

Oder so.
"(?:^TEXT.*;)(.*)"

.. mit ?: wird die Klammer zwar auch gesucht, aber nicht ausgegeben ;)

von Christoph M. (mchris)


Lesenswert?

von 🐧 DPA 🐧 (Gast)
>Ich würde da gar keine regex verwenden. Einfach passend splitten:

Normalerweise mache ich es auch zu Fuß. Regex scheint recht mächtig, 
aber schwer nachzuvollziehen.
Ich wollte aber mal was neues ausprobieren, deshalb mal mit regex.
Das Ganze soll aber nicht in der bash sondern in Python verwendet 
werden.

TotoMitHarry (Gast)
>.. mit ?: wird die Klammer zwar auch gesucht, aber nicht ausgegeben ;)

Geht aber aber in folgendem Code nicht:
1
import re
2
3
txt = ""
4
txt = txt + "SYMBOL voltage -128 160 R0" +"\n"
5
txt = txt + "SYMATTR InstName V1" +"\n"
6
txt = txt + "SYMATTR Value SINE(0 1 1000)" +"\n"
7
txt = txt + "TEXT -160 408 Left 2 !.tran 5m" +"\n"
8
txt = txt + "TEXT 160 352 Left 2 ;Label  size 1.5"+"\n"
9
txt = txt + "TEXT 160 408 Left 3 ;Label size 2.0"+"\n"
10
txt = txt + "TEXT 160 480 Left 4 ;Label size 3.5"+"\n"
11
txt = txt + "TEXT 152 544 Left 4 ;Label size 3.5"+"\n"
12
txt = txt + "TEXT 688 256 VLeft 2 ;amplitude (vertical)"+"\n"
13
txt = txt + "RECTANGLE Normal 880 784 80 -32 2" +"\n"
14
15
x = re.findall(r"TEXT.*;(.*)", txt) # geht  
16
#x = re.findall(r"(?:^TEXT.*;)(.*)", txt) #geht nicht 
17
18
print(x)
19
20
for s in x:
21
  print(s)

von Norbert (Gast)


Lesenswert?

1
#!/usr/bin/python3
2
# -*- coding: UTF-8 -*-
3
4
import re
5
6
data = '''
7
SYMBOL voltage -128 160 R0
8
WINDOW 123 0 0 Left 2
9
WINDOW 39 0 0 Left 2
10
SYMATTR InstName V1
11
SYMATTR Value SINE(0 1 1000)
12
TEXT -160 408 Left 2 !.tran 5m
13
TEXT 296 272 Left 2 ;time
14
TEXT 688 256 VLeft 2 ;amplitude (vertical)
15
TEXT 216 16 Left 2 ;LtSpice Labels
16
TEXT 160 352 Left 2 ;Label  size 1.5
17
TEXT 160 408 Left 3 ;Label size 2.0
18
TEXT 160 480 Left 4 ;Label size 3.5
19
TEXT 152 544 Left 4 ;Label size 3.5
20
RECTANGLE Normal 880 784 80 -32 2
21
'''
22
23
pattern = re.compile(r"^TEXT.*;(.*)")
24
25
for line in data.splitlines():
26
    result = pattern.match(line)
27
    if result:
28
        print(result.group(1))
1
time
2
amplitude (vertical)
3
LtSpice Labels
4
Label  size 1.5
5
Label size 2.0
6
Label size 3.5
7
Label size 3.5

von Christoph M. (mchris)


Lesenswert?

Danke dafür.
Jetzt habe ich schon das nächste Problem: Gesucht werden soll über 
mehrere Zeilen ..
1
Version 4
2
SHEET 1 948 804
3
WIRE -128 176 -128 128
4
WIRE -128 304 -128 256
5
FLAG -128 304 0
6
SYMBOL voltage -128 160 R0
7
WINDOW 123 0 0 Left 2
8
WINDOW 39 0 0 Left 2
9
SYMATTR InstName Spannung1
10
SYMATTR Value SINE(0 1 1000)
11
SYMBOL button 224 288 R0
12
SYMATTR InstName button1
13
SYMBOL button 224 336 R0
14
SYMATTR InstName button2
15
SYMBOL button 224 384 R0
16
SYMATTR InstName button3
17
TEXT -160 408 Left 2 !.tran 5m
18
TEXT 216 16 Left 2 ;LtSpice Labels
19
TEXT 224 232 Left 2 ;Selector
20
RECTANGLE Normal 880 784 80 -32 2

Die Definition eines Symbols verteilt sich über mehrere Zeilen (z.B. 2)
1
SYMBOL button 224 288 R0
2
SYMATTR InstName button1

Ich will den Namen "button1" aus den zwei Zeilen extrahieren. Das 
bedeutet also, man muss zuerst die Zeile mit dem Inhalt "SYMBOL button" 
und dann aus der Zeile mit "SYMATTR InstName" den Namen "button1" 
extrahieren.

Wenn ich als Regex-Expression folgendes nehme
1
(^SYMBOL button(.*)\nSYMATTR InstName).*
kommen als Matches immer beide Zeilen gleichzeitig, aber nicht der 
gesuchte Namen.

von Norbert (Gast)


Lesenswert?

Python regex kann auch problemlos über mehrere Zeilen und man kann 
mehrere groups definieren.

Vielleicht ist noch nicht einmal garantiert, das die Zeilen immer in der 
von dir gewünschten Reihenfolge an die Tür klopfen.
Irgendwann wird es jedoch sinnvoller sein, eine kleine Klasse zu 
schreiben und etwas zusätzliche Logik zu implementieren.
Wer zu Klassen eine eher abweisende Grundeinstellung hat, nimmt dann 
eben closures.

All das zumal dieses aktuelle Problem wahrscheinlich nicht deine letzte 
Herausforderung sein wird.

von Christoph M. (mchris)


Lesenswert?

>Vielleicht ist noch nicht einmal garantiert, das die Zeilen immer in der
>von dir gewünschten Reihenfolge an die Tür klopfen.
>Irgendwann wird es jedoch sinnvoller sein, eine kleine Klasse zu
>schreiben und etwas zusätzliche Logik zu implementieren.

Vielleicht hast Du recht und ich sollte es vielleicht ohnehin ohne regex 
machen. Das ist für mich etwas eingängiger.

Das Format eine *.asc LtSpice Files ist etwas seltsam: Erst kommt die 
Symbolbezeichung und in den nachfolgenden Zeilen die Attribute. Alle 
Zeilen gehören dazu bis zur nächstmöglichen nächsten Symbolbezeichnung. 
Um die Endzeile eines Symbols zu erkennen muss man also erkennen das ein 
neues Symbol anfängt. Es können aber auch andere Elemente wie "TEXT" das 
Ende eines Symbols markieren. Ich weiß gar nicht, ob solch ein komplexer 
Parser mit Regex überhaupt möglich ist.

1
#!/usr/bin/python3
2
# -*- coding: UTF-8 -*-
3
4
import re
5
6
data = '''
7
Version 4
8
SHEET 1 948 804
9
WIRE -128 176 -128 128
10
WIRE -128 304 -128 256
11
FLAG -128 304 0
12
SYMBOL voltage -128 160 R0
13
WINDOW 123 0 0 Left 2
14
WINDOW 39 0 0 Left 2
15
SYMATTR InstName Spannung1
16
SYMATTR Value SINE(0 1 1000)
17
SYMBOL button 224 288 R0
18
SYMATTR InstName button1
19
SYMBOL button 224 336 R0
20
SYMATTR InstName button2
21
SYMBOL button 224 384 R0
22
SYMATTR InstName button3
23
TEXT -160 408 Left 2 !.tran 5m
24
TEXT 216 16 Left 2 ;LtSpice Labels
25
TEXT 224 232 Left 2 ;Selector
26
RECTANGLE Normal 880 784 80 -32 2
27
'''
28
29
pattern = re.compile(r"(^SYMBOL button(.*)\\nSYMATTR InstName)(.*)")
30
print(pattern.match(data))

von Norbert (Gast)


Lesenswert?

Christoph M. schrieb:
> und ich sollte es vielleicht ohnehin ohne regex
> machen.

Gibt kaum etwas schöneres und flexibleres als RegEx.
Lies Zeilenweise ein, kompiliere dir verschiedene RegEx-Pattern, wende 
sie an und sammle die Ergebnisse. Wenn alle Zeilen eines Blocks gelesen 
sind, dann die Infos kombinieren.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.