Zwie Textdateien in Python einlesen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Niccolo
User
Beiträge: 7
Registriert: Freitag 17. Oktober 2014, 11:51

Hallo an alle

Also ich bin ein Neuling bei der Programmiersprache Python, habe aber Grundgendnisse in anderen Programmiersprachen.

Also mein Problem sieht so aus. Ich möchte aus zwei verschiedene Textdateien Daten in Python einlesen lassen. Die erste Datei liest er auch ohne Problem ein, aber bei der Zweite Datei bricht er mit dem Fehler ReferenceError: could not find 'main' in tag 'Null'
Habe mich schon im Netz nach einer Lösung gesucht.
Hier der Code:

Code: Alles auswählen

import c4d
#Welcome to the world of Python

#
# lese eine Ascii-File und erzeuge ein Objekt
#

def ReadAscii():
    
    legoNamen = "Brick 1x1"
    FehlerDaten = "Leider konnte ich den Legostein nicht konstruieren, \nda mir die Datei mit den Daten fehlen!"
    
    pointFile = "d:\Temp\Test\Brick1x1_Punkte.txt"
    try:
        fpoints = open(pointFile)
    except IOError:
        c4d.gui.MessageDialog(FehlerDaten)
        return
    
    polysFile = "d:\Temp\Test\Brick1x1_Polygone.txt"
    try:
        fpolys = open(polysFile)
    except IOError:
        c4d.gui.MessageDialog(FehlerDaten)
        return    

    #teile die Datei in eine Linen-Liste
    lines = fpoints.readlines()
    #wieviele Linien? pro Punkt
    nrPoints = len(lines)
    
     #teile die Datei in eine Linen-Liste
    linesPolys = fpolys.readlines()
    #wieviele Linien? pro Polygon
    nrPolys = len(linesPolys)

    #-1 for erste Linie
    print "Nr. points-polys: ", nrPoints-1, nrPolys-1
    
    #erzeuge eine Polygon Objekt mit Punktezähler der Linien
    rebrick = c4d.BaseObject(c4d.Opolygon)
    rebrick.ResizeObject(nrPoints-1, nrPolys-1)
    
    #Punkte
    for i, line in enumerate(lines):
        #teile die Linien in Komponente
        coord = line.split(",")
        #ignoriere korrupte csv Linie
        if len(coord)<4 : continue
        
        try:
            nr = int(coord[0])
            x = float(coord[1])
            y = float(coord[2])
            z = float(coord[3])
        except ValueError:
            continue    
        
        rebrick.SetPoint(i-1,c4d.Vector(x,y,z))
        print "Punkt eingefügt: ", i-1,x,y,z
        
    #Polygone
    for i, line in enumerate (linesPolys):
        #teile die Linien in Komponente
        coord = line.split(",")
        #ignoriere korrupte csv Linie
        if len(coord)<5 : continue
        
        try:
            nr = int(coord[0])
            p1 = int(coord[1])
            p2 = int(coord[2])
            p3 = int(coord[3])
            p4 = int(coord[4])
        except ValueError:
            continue    
        
        rebrick.SetPolygon(nr,c4d.CPolygon(p1,p2,p3,p4))
        print "Polygon eingefügt: ", nr,x,y,z
        
    doc.InsertObject(rebrick)
    rebrick.Message (c4d.MSG_UPDATE)
    c4d.EventAdd()
    
if __name__=='__main__':
    ReadAscii()
Den Code habe von ein Tutorial.
Ich hoffe das ihr mir einen Tipp geben könnt wo der Schuh im den Code drückt

Gruss Andreas
BlackJack

@Niccolo: Wie sieht denn der komplette Traceback aus? Daran sieht man ja welche Zeile die Ausnahme auslöst.

Ich könnte mir vorstellen das die ignorierende Fehlerbehandlung Probleme bereitet falls es wirklich korrupte Zeilen geben sollte. Man hat bei dem `ResizeObject()` ja angegeben wie viele Punkte und Polygone kommen, vielleicht müssen dann auch tatsächlich so viele gesetzt werden. Und wenn ein Punkt übersprungen wird, dann verschieben sich ja alle anderen und die Referenz bei den Polygonen bezieht sich dann auf falsche oder sogar nicht existierende Punkte.

Ein Unterschied zwischen den beiden Dateien ist, dass bei den Punkten die erste Spalte (`nr`) ignoriert wird, während bei der Polygondatei die Laufvariable `i` nicht verwendet wird. Und beim Text für ein eingefügtes Polygon werden immer wieder die selben x, y, und z ausgegeben die aus der vorherigen Schleife stammen. Das sieht nach kopieren und einfügen und dann leicht verändern aus. Das macht man nicht, denn das ist ein deutliches Zeichen dafür, dass man eigentlich eine Funktion daraus machen möchte.

Sonstige Anmerkungen zum Quelltext: Ein Blick in den Style Guide for Python Code wäre nicht schlecht.

Der Kommentar über der Funktion würde sich als DocString besser machen.

`legoNamen` wird definiert aber nirgends verwendet.

`pointFile` und `polysFile` müsste man entweder nicht als Namen definieren, oder man sollte es als Argumente machen. Und der Name ist auch falsch da es sich bei beiden gar nicht um Datei-Objekte handelt sondern um Datei*namen*. Die beiden Namen wären eher passend für die Objekte die Du `fpoints` und `fpolys` genannt hast.

Wenn man noch eine Hauptfunktion dazwischen schaltet, dann könnte man dort den `IOError` *einmal* behandeln, statt bei beiden Dateien den gleichen Code dafür zu schreiben.

Dateien die man öffnet sollte man auch wieder schliessen. Da bietet sich die ``with``-Anweisung an um das sicher zu stellen.

Das Einlesen der beiden Dateien ist ungünstig im Code verteilt. Statt beide Dateien zu öffnen um danach dann beide komplett in den Speicher zu lesen, um danach dann beide zu parsen vermischt fast identische Verarbeitung von zwei Dateien. Dabei könnte man ganz prima eine Funktion schreiben die so eine Datei einliest und parst und die einfach zweimal aufrufen, statt zweimal den Code dort hinzuschreiben.

Statt die erste Zeile durch ”Fehlerbehandlung” zu ignorieren und an diversen Stellen die Zeilenanzahl - 1 zu rechnen, könnte man die erste Zeile beim Einlesen auch einfach überspringen. In der ersten Schleife wird übrigens der Punkt mit der Nummer -1 gesetzt — soll das so sein‽

`doc` wird einfach so benutzt, wird aber nirgends definiert‽ Wo kommt das her?

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
import csv
import c4d


def parse_csv(filename, parse_funcs, skip_header=True):
    with open(filename, 'rb') as lines:
        reader = csv.reader(lines)
        if skip_header:
            next(reader)
        for row in reader:
            yield [parse(cell) for parse, cell in zip(parse_funcs, row)]


def read_object_from_csv(points_filename, polygons_filename):
    """Liest Objekt aus zwei CSV-Dateien."""

    points = list(parse_csv(points_filename, (int, float, float, float)))
    polygons = list(parse_csv(polygons_filename, (int, int, int, int)))

    rebrick = c4d.BaseObject(c4d.Opolygon)
    rebrick.ResizeObject(len(points), len(polygons))

    for i, (_, x, y, z) in enumerate(points):
        rebrick.SetPoint(i, c4d.Vector(x, y, z))

    for i, (number, p_1, p_2, p_3, p_4) in enumerate(polygons):
        rebrick.SetPolygon(number, c4d.CPolygon(p_1, p_2, p_3, p_4))

    doc.InsertObject(rebrick)   # Bug?
    rebrick.Message(c4d.MSG_UPDATE)
    c4d.EventAdd()


def main():
    filename_template = r'd:\Temp\Test\Brick1x1_{0}.txt'
    try:
        read_object_from_csv(
            filename_template.format('Punkte'),
            filename_template.format('Polygone')
        )
    except IOError:
        c4d.gui.MessageDialog(
            'Leider konnte ich den Legostein nicht konstruieren,\n'
            'da mir die Datei mit den Daten fehlen!'
        )


if __name__ == '__main__':
    main()
Niccolo
User
Beiträge: 7
Registriert: Freitag 17. Oktober 2014, 11:51

Hi BlackJack

Vielen Dank für deine ausfühliche Antwort. Da kann ich einiges Umstellen und verbessern. Das kommt davon wenn man von jemand anderes einen Code abtippt und nicht weis was man da eigentlich macht.
In der zwischenzeit habe ich den Fehler gefunden, es lage an der Datei Polygone, als Beispiel hat der Typ ein Quadrat verwendet. Dort gibt es vier Polygone + Nummer.
Bei meinen Legostein nur 3 Polygone + Nummer. Ergo fehlt ihn eine Zahl und schon stimmt die Reihenfolge nicht mehr. :D

Jetzt zu der Variabel doc.
Die gehört zu Cinema 4D damit man die aktuelle Zeichnung (Modell) bearbeiten kann. Cinema 4D bezeichtet dies als Documend.
Python ist nämlich ein Teil von C4D um eigene Funktionen oder andere Sachen damit zu programmieren.
Ich habe vor eine Lego-Datenbank aufzubauen, damit ich nicht immer in andere Programme die Legosteine (Modelle) zu entwerfen, sie dann in ein passendes Format exportieren und dann wieder in C4D einfügen.

Die beiden Dateien enthalten die Fixpunkte sowie die Polygone von dem Legostein 1x1.
Der Aufbau der Datei "Punkt" sieht so aus:
Punkt X Y Z
0, 0.24, 0.8, 0.24
1, -0.24, 0.8, 0.24
usw.

Und der Aufbau der zweiten Datei "Polygone" so
Polygon A B C D
0, 1, 2, 3
1, 3, 0, 1
usw.
der Polygon unte D gibt es nicht bei dem Legostein

Die Variabel LegoNamen brauche ich später, das gibt dann der Polygon Name in C4D.

Jetzt habe ich noch eine andere Frage:
Ist es vielleicht möglich das ich die beiden Dateien in eine Datei Stecke und dann die Routine so um schreibe damit er weis wann die Punkte fertig sind und die Polygone anfangen

Gruss Niccolo
BlackJack

@Niccolo: Klar kann man die Informationen auch in einer Datei zusammenfassen. Wo kommen die Daten denn überhaupt her? Es gibt ja mehrere spezielle Lego-CAD-Programme und damit eigentlich auch alle Bausteine schon in irgendeiner Form als 3D-Modelle. Würde es nicht Sinn machen so eine schon bestehende Teilebibliothek zu konvertieren? Und vielleicht auch nicht unbedingt in einem laufenden C4D sondern in einem gängigen Dateiformat für 3D-Daten was von C4D gelesen werden kann? Eventuell gibt das sogar schon fertig — kann mir kaum vorstellen dass Du der erste bist der so etwas machen möchte.
Niccolo
User
Beiträge: 7
Registriert: Freitag 17. Oktober 2014, 11:51

Hi BlackJack

Nein der erste bin ich nicht, wo sowas vor hat.
Ich habe auch vorgehabt die 3D-Daten von den Legoprogramm LDraw zu benutzen, da diese als Dat-Daten vorliegen und man sie wie eine Textdatei lesen kann.
Das ganze hat aber einen Haken, C4D kann mit dem Aufbau nichts anfangen. Du kennst es doch jedes Programm hat seine eigene Rezepte.
Die meisten Legoprogramme habe auch einen Export nach 3DS, und die kann auch C4D einlesen. Nur haben nicht alle Legoprogramme alle Legosteine.
Zum Beispiel hat das Legoprogramm "LEGO Digital Designer" das direkt von Lego stammt alle Steine, nur hat es keinen Export nach 3DS sondern, nach einen anderen Legoprogramm.
Der Export funktioniert aber nicht richtig. Ich habe im Grunde genommen immer die Legosteine die brauche, nach C4D exportieren lassen und habe dann dort das Modell zusammen gebaut.
Mich hat das immer gestört, immer mit mehrere Programme zu handieren.
Was die meisten wohl abschreckt, ist die komlette 3D-Daten Bank frisch zu machen.
Ich gehe folgender maße vor.
Ich lesse ein Stein in C4D ein.
Cinema kann dann alle Daten die brauche in eine Textdatei abspeichern. Somit habe ich mal die 3D-Daten für einen Stein.
Meinen Überlegung war dann nur noch, wie kann ich die Daten wieder einlesen, damit ich den Stein wieder als Objekt für C4D habe.
Soweit habe ich die Leseroutine nun, Dank deiner Hilfe. Muss Sie nur noch begreifen, damit ich Sie immer wieder anpassen kann. Habe nämlich festgestellt das C4D nicht immmer alle Spalten für ein Stein braucht. Also muss ich die Routine noch soweit Modular umgestalten, das ich am Anfang der Datei in folgende Parameter noch übergeben muss, aus wieviel Spalten die Punkte und Polygon-Daten bestehen.
Und wenn das funktioniert, geht es weiter. Dann muss ich das ganze noch als Plug-In schreiben für C4D.
Aber einen Schritt nach dem anderen. Muss Python erst mal richtig verstehen lernen. Deine Routine bereitet mir schon Kopf zerbrechen, wie das alles zusammen hängt.

Gruss Niccolo
BlackJack

@Niccolo: Ich bin ein wenig verwirrt. Wo kommen Deine Daten für einen Stein denn her wenn nicht aus einer bereits bestehenden Bauteilesammlung in irgendeinem entsprechenden Format?

Das C4D mit dem LDraw-Format direkt nichts anfangen kann ist nicht so verwunderlich, aber Du schreibst doch anscheinend gerade einen Konverter (?), warum also nicht einen der LDraw-Dateien einlesen und umwandeln kann? Sofern es so etwas nicht schon gibt natürlich nur. Ich kann mir irgendwie nicht vorstellen dass das in all den Jahren/Jahrzehnten, diese Programme/Formate gehen ja bis in die gute alte DOS-Zeit zurück, nicht schon mal irgendwer gemacht hat. Hast Du schon im entsprechenden LDraw-Forum mal recherchiert?
Niccolo
User
Beiträge: 7
Registriert: Freitag 17. Oktober 2014, 11:51

Hi

Etwas spät die Antwort, aber ich war über das Wochenend unterwegs.

Also eine Legostein-Datei sieht in LDraw etwa so aus
0 Hinge Arm Locking with Dual Finger and Axlehole
0 Name: 30553.dat
0 Author: Franklin W. Cain [fwcain]
0 !LDRAW_ORG Unofficial_Part
0 !LICENSE Redistributable under CCAL version 2.0 : see CAreadme.txt

0 BFC CERTIFY CW

0 !KEYWORDS click-hinge, Life on Mars, Studio, Technic, Star Wars

0 !HISTORY 2003-07-02 [technog] added BFC
0 !HISTORY 2004-03-02 [PTadmin] Official Update 2004-01
0 !HISTORY 2004-03-11 [cwdee] Correct title
0 !HISTORY 2004-04-22 [PTadmin] Official Update 2004-02
0 !HISTORY 2007-07-09 [PTadmin] Header formatted for Contributor Agreement
0 !HISTORY 2008-07-01 [PTadmin] Official Update 2008-01
0 !HISTORY 2008-07-07 [PTadmin] Renamed from 482
0 !HISTORY 2012-10-05 [Philo] Corrected bleeding

1 16 0 0 0 1 0 0 0 1 0 0 0 1 s\480s02.dat
1 16 0 0 0 1 0 0 0 1 0 0 0 -1 s\30552s01.dat
1 16 0 0 0 2.12132 0 -2.12132 -2.12132 0 -2.12132 0 1 0 1-4ring2.dat
1 16 0 0 0 -2.12132 0 2.12132 2.12132 0 2.12132 0 1 0 1-4ring2.dat
3 16 6.364 -6.364 0 6.607 -6 0 6 -6 0
3 16 -6 -6 0 -6.607 -6 0 -6.364 -6.364 0
3 16 6 6 0 6.607 6 0 6.364 6.364 0
3 16 -6.364 6.364 0 -6.607 6 0 -6 6 0
Auf der Seite von LDraw wird auch erklärt wie die Daten zu verstehen sind.
Aber damit kann ich aber kein Script schreiben um das in C4D umzusetzen. Wollen wir es mal so ausdrücken - Ich kann das nicht.
Und auf so eine Schnittstellen warte ich schon ziemlich lange. Also bevor ich noch lange warten muss, erzeuege ich mir meine eigen 3D-Daten Bank für Legosteine. :D

Gruss Niccolo
BlackJack

@Niccolo: Das erklärt immer noch nicht wo Du die Daten herbekommst für die Dateien die Du importierst‽
Niccolo
User
Beiträge: 7
Registriert: Freitag 17. Oktober 2014, 11:51

Hi BlackJack

Also ich werde jeden einzelen Stein von einen Legoprogramm exportieren.
Dann werde ich diese Steine wieder in C4D einlesen. Auf C4D kann ich nämilch die Punkte, Polygone und Texturen Daten als Textdatei abspeichern das dann so ausschaut
Polygon A B C D
0 1 2 3
1 3 0 1
2 1 0 4
.
.
.

Punkt X Y Z
0 0.24 cm 0.8 cm 0.24 cm
1 -0.24 cm 0.8 cm 0.24 cm
2 -0.24 cm 0.8 cm -0.24 cm
3 0.24 cm 0.8 cm -0.24 cm
4 0.24 cm 0 cm 0.24 cm
.
.
.
Somit bekomme die Daten, damit ich mir meine 3D-Dtenbank aufbauen kann. Es wird zwar lange daueren bis ich alle Steine habe, aber am Ende haben alle was davon.

Gruss Niccolo
wagneru
User
Beiträge: 29
Registriert: Freitag 3. Januar 2014, 13:55
Wohnort: Groß-Gerau

@niccolo

Das ist jetzt nicht Böse gemeint, aber ich würde Dir erst einmal raten, Deutsch zu lernen. In jedem Satz mindestens ein Grammatikfehler. Nur einige Beispiele:

"bei der Programmiersprache"
"Grundgendnisse"
"bei der Zweite Datei"
"Habe mich schon im Netz nach einer Lösung gesucht"
"wo der Schuh im den Code drückt"
"und nicht weis was man da eigentlich macht"
"bezeichtet dies als Documend"
"wo sowas vor hat"

Das macht mich irgendwie sehr traurig.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

wagneru hat geschrieben:Das ist jetzt nicht Böse gemeint, aber ich würde Dir erst einmal raten, Deutsch zu lernen.
Ich würde dir raten, so etwas per PM zu klären. Für mich waren die Texte durchaus verständlich und daher reicht es mir, wenn der Fragesteller erst einmal den korrekten Umgang mit Python lernt. Wenn ich bei StackOverflow frage, dann hoffe ich auch auf Antworten, obwohl mein Englisch sicher nicht perfekt ist.
Antworten