@ernd59: Angenommen es gibt keine fertige Bibliothek, was ich fast nicht glauben kann, musst Du Dir die einzelnen Teilschritte überlegen die gelöst werden müssen um von dieser Datei zu einer zu kommen die Du haben möchtest. Dabei zerlegst Du das Problem in immer kleinere Teilprobleme bis die so einfach sind, das man eine Funktion mit ein paar Zeilen Code schreiben kann um sie zu lösen.
Das Gesamtproblem kann man ja zum Beispiel in die beiden Schritte a) einlesen der Spice-Datei und b) schreiben der ”Tabelle” aufteilen. Als Datenstruktur im Programm eignet sich eine Liste die eine Liste pro Zeile der Ausgabedatei enthält. Man kann also eine Funktion `convert_spice_to_csv()` schreiben die beide Dateinamen bekommt, eine `load_spice()`-Funktion aufruft, welche die Datei einliest und als Zeilenliste zurück gibt, und dann eine Funktion `save_csv()` aufruft, die diese Zeilenliste in eine CSV-Datei mit Tabulatorzeichen als Spaltentrenner schreibt:
Code: Alles auswählen
def convert_spice_to_csv(spice_filename, csv_filename):
save_csv(csv_filename, load_spice(spice_filename))
Die `save_csv()` ist mit hilfe des `csv`-Moduls aus der Standardbibliothek in drei bis vier Zeilen Code schnell geschrieben.
Das lesen der Spice-Datei ist etwas komplizierter, also kann man das wieder in Teilschritte zerlegen die in einzelnen Funktionen gelöst werden. Man muss ja als erstes herausfinden wie die Variablen heissen. Also könnte man in der `read_spice()`-Funktion die Datei öffnen und eine weitere Funktion schreiben die diese Informationen ausliest und als Liste mit Variablennamen liefert. Das ist die erste Liste im Ergebnis. Was dann im nächsten Schritt durch die Werte erweitert werden kann. Die Funktion sieht auch einfach und überschaubar aus:
Code: Alles auswählen
def load_spice(filename):
with open(filename, 'r') as lines:
rows = [read_variable_names(lines)]
rows.extend(read_values(lines))
return rows
Was muss man nun tun um die Variablennamen zu lesen? Als erstes muss man die Datei zeilenweise lesen bis man zu den Variablennamen kommt. Das erkennt man an der 'Variables:'-Zeile. Dann liest man solange Zeilen und bis man zur ersten Leerzeile kommt, und liest dabei aus jeder Zeile den Variablennamen aus und fügt den der Ergebnisliste in dieser Funktion hinzu. Auch das ist mit ein paar Zeilen Code abgehandelt:
Code: Alles auswählen
def read_variable_names(lines):
for line in lines:
if line.rstrip() == 'Variables:':
break
variable_names = list()
for line in lines:
if not line.rstrip():
break
parts = line.split('\t')
variable_names.append(parts[2])
return variable_names
Als nächstes kommt das lesen der Werte. Da muss man solange Zeilen überlesen bis man bei der 'Values:'-Zeile angekommen ist. Hier ergibst sich dann die Möglichkeit den bisherigen Code zu überarbeiten, denn so etwas ähnliches hat man in `read_variable_names()` auch schon, nur mit einem anderen Wert. Das kann man also in eine eigene Funktion heraus ziehen, die von den anderen beiden Funktionen verwendet wird:
Code: Alles auswählen
def skip_until(lines, value):
for line in lines:
if line.rstrip() == value:
break
else:
raise ValueError('line with {0!r} not found'.format(value))
def read_variable_names(lines):
skip_until(lines, 'Variables:')
variable_names = list()
for line in lines:
if not line.rstrip():
break
parts = line.split('\t')
variable_names.append(parts[2])
return variable_names
Das Einlesen der Werte kann man wieder in ein Teilproblem zerlegen, nämlich das einlesen eines einzelnen Werteblocks. Diese Funktion ruft man dann so lange auf bis alle Blöcke gelesen sind, was sie durch den Rückgabewert `None` anzeigt:
Code: Alles auswählen
def read_values(lines):
skip_until(lines, 'Values:')
rows = list()
while True:
row = read_value_block(lines)
if row is None:
break
rows.append(row)
return rows
Ein einzelner Werteblock kann mit Leerzeilen anfangen, die man überlesen muss, und dann muss man bis zur nächsten Leerzeile die Werte aus den einzelnen Zeilen herauslesen und in eine Ergebnisliste stecken. Wenn beim überlesen der Leerzeilen festgestellt wird das keine anderen Zeilen mehr folgen, gibt man `None` zurück um dem Aufrufer das Ende der Werte zu signalisieren:
Code: Alles auswählen
def read_value_block(lines):
line = ''
for line in lines:
if line.rstrip():
break
if not line.rstrip():
return None
_, _, value = line.partition('\t')
row = [value.strip()]
for line in lines:
if not line.rstrip():
break
_, _, value = line.partition('\t')
row.append(value.strip())
return row
Auf diese Weise kann man so ziemlich jedes grössere Problem auf viele kleinere Probleme herunterbrechen, die sich mit kleinen, nicht allzu komplexen Funktionen lösen lassen.
Nochmal komplett:
Code: Alles auswählen
import csv
def skip_until(lines, value):
for line in lines:
if line.rstrip() == value:
break
else:
raise ValueError('line with {0!r} not found'.format(value))
def read_variable_names(lines):
skip_until(lines, 'Variables:')
variable_names = list()
for line in lines:
if not line.rstrip():
break
parts = line.split('\t')
variable_names.append(parts[2])
return variable_names
def read_value_block(lines):
line = ''
for line in lines:
if line.rstrip():
break
if not line.rstrip():
return None
_, _, value = line.partition('\t')
row = [value.strip()]
for line in lines:
if not line.rstrip():
break
_, _, value = line.partition('\t')
row.append(value.strip())
return row
def read_values(lines):
skip_until(lines, 'Values:')
rows = list()
while True:
row = read_value_block(lines)
if row is None:
break
rows.append(row)
return rows
def load_spice(filename):
with open(filename, 'r') as lines:
rows = [read_variable_names(lines)]
rows.extend(read_values(lines))
return rows
def save_csv(filename, rows):
with open(filename, 'wb') as csv_file:
writer = csv.writer(csv_file, delimiter='\t')
writer.writerows(rows)
def convert_spice_to_csv(spice_filename, csv_filename):
save_csv(csv_filename, load_spice(spice_filename))
def main():
convert_spice_to_csv('Differenzeingangsstufe3.txt', 'test.csv')
if __name__ == '__main__':
main()
Das macht aus Deiner Eingabedatei:
Code: Alles auswählen
time V(vin1) V(vb+)
0.000000000000000e+000 0.000000000000000e+000 8.000000000000000e+000
9.999999717180685e-010 0.000000000000000e+000 8.000000000000000e+000
1.999999943436137e-009 0.000000000000000e+000 8.000000000000000e+000