Anfängerfragen, auslesen von Dateien, Inhalte bearbeiten

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
kistenschieber
User
Beiträge: 2
Registriert: Donnerstag 12. Dezember 2013, 16:36

Hallo Forum,

als nicht mehr ganz so taufrischer Neueinsteiger in das Thema Programierung habe ich gerade mal wieder einen Durchhänger.

Ich möchte folgendes erreichen:

Eine Datei soll ausgelesen werden, Struktur wie folgt:

A;2
B;1
C;4
u.s.w

Hier liegt also immer ein Paar vor. Ein Wert vor dem Trenner und ein Wert dahinter. Dieser zweite Wert ist immer nummerisch.

Das Script soll den Inhalt bearbeiten und folgenden Inhalt in eine Datei ausgeben:

A
A
B
C
C
C
C
u.s.w

Der erste Wert soll also so oft wiederholt in einer neuen Zeile ausgegeben werden, wie es der zweite Wert vorgibt.

Das einlesen der Datei bekomme ich, glaube ich, hin:

Code: Alles auswählen

items = [] 
fobj = open("datei.csv", "r") 
for line in fobj: 
    line = line.strip() 
    wert = line.split(";") 
    items[wert[0]] = wert[1] 
fobj.close()
Damit sollte doch der Inhalt der Datei in einem Array landen?

Danach muss das ganze irgendwie in einer Schleife abgearbeitet werden. Auslesen der Wertepaare, Zählfunktion zur Ausgabe der Werte in n Zeilen...

Und da fällt bei mir der Vorhang, hier hab ich den Knoten im Hirn.

Wie realisiere ich sowas?

Und nein, ich möchte _nicht_ das ihr die Arbeit für mich macht, ich würde die Materie wirklich gerne begreifen.

Ein Beispiel mit Erklärungen würde mir helfen.

Dank an alle die bis hierher gelesen haben, eventuell kann mir ja jemand helfen.

Gruß

Kistenschieber
Zuletzt geändert von Anonymous am Donnerstag 12. Dezember 2013, 17:44, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
BlackJack

@kistenschieber: Wie kannst Du glauben das Du das einlesen so wie gezeigt hinbekommst, wenn dieser Code ganz offensichtlich *nicht* läuft. Du versuchst einen Buchstaben als Index in eine leere Liste zu verwenden. Das macht absolut keinen Sinn.

Für die Datei würde ich die ``with``-Anweisung empfehlen, damit braucht man die dann nicht mehr explizit schliessen und es ist sichergestellt, dass sie am Ende des ``with``-Blocks auf jeden Fall geschlossen wird.
self.made_man
User
Beiträge: 7
Registriert: Mittwoch 11. Dezember 2013, 19:37

Weil du mit wert[0] einen String erhälst, wird kein Array sondern ein Dictionary aufgebaut. Ich würde es allerdings nicht nacheinander, sondern gleichzeitig machen. Öffne zwischen Zeile 2 und 3 die Ausgabedatei und schreibe in Zeile 6 direkt in die Ausgabedatei hinein. Als Zeile 8 musst du diese natürlich ebenfalls schließen, bist dann aber schon fertig.

Der Nachteil an einem Dictionary wäre, dass es keine festgelegt Reihenfolge gibt. Aus

Hund;2
Katze;1
Maus;3

könnte so werden:

Maus
Maus
Maus
Katze
Hund
Hund

Ob das in deinem Fall egal ist musst du selbst entscheiden. Verarbeitest du direkt, ohne Umweg über ein extra Dictionary, bleibt die Reihenfolge erhalten.

Noch ein Hinweis. Man kann Methoden auch auf Methoden anwenden, wenn sie das gleiche Objekt als Rückgabe erzeugen. Also geht auch

Code: Alles auswählen

wert = line.strip().split(";")
Denn strip() liefert einen String, den split() dann direkt nutzt.
self.made_man
User
Beiträge: 7
Registriert: Mittwoch 11. Dezember 2013, 19:37

BlackJack hat Recht, es muss

Code: Alles auswählen

items = {}
heißen, damit ein Dictionary erstellt wird. Mit einer Liste geht es nicht.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Hallo,

Da Du eine CSV-Datein einliest, würde ich Dir einfach das ``csv``-Modul aus der Standard Lib nahelegen!

Ausgehend von einer Datenstruktur wie dieser

Code: Alles auswählen

values = (('A', 2), ('B', 1), ('C', 4))
kannst Du auch die Funktion ``itertools.repeat`` nutzen, welche genau das entscheidende tut: Dinge wiederholen. Dazu musst Du einfach jedes Element der Struktur "anfassen" und die Funktion mit den beiden Werten aufrufen:

Code: Alles auswählen

 list(repeat('A', 2))
 > ['A', 'A']
Die Rückgabe kannst Du einfach mittels ``"\n".join(*hier den Funktionsaufruf von repeat*)`` zu einem String zusammenfügen.

Die obere Struktur erhältst Du einfach durch das CSV-Modul (Ok, es sind Listen von Listen und keine Tupel, aber das ist für den lesenden Zugriff unerheblich).

Letztlich musst Du dann nur noch die einzelnen Stücke in einer Datei schreiben und jeweils für den Umbruch sorgen.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@self.made_man: Das mit dem Methoden auf Methoden anwenden kling ein wenig missverständlich. Man kann eine Methode auf einem Objekt aufrufen. Dazu holt man sich mit dem Punktoperator die Methode vom Objekt und ruft die dann mit dem Aufrufoperator auf, also zum Beispiel ``some_object . method_name (arg1, arg2)``, wobei das gültige Syntax ist, die Leerzeichen um den Punktoperator und zwischen Methodenname und Aufrufoperator der Veranschaulichung dienen. Der gesamte Ausdruck wird ausgewertet und ergibt wieder ein Objekt. Das vielleicht wieder Methoden hat. Und die Methoden kann man dann genau so wieder mit dem Punktoperator, dem Methodennamen, und Aufrufoperator aufrufen. `some_object` muss kein Name sein, das kann ein beliebiger Ausdruck sein der zu einem Objekt ausgewertet wird.

Oder um es mal mit Rechenoperationen zu erklären: man kann nicht nur ``a + b`` schreiben, sondern natürlich auch ``a + b + c`` wobei erst ``a + b`` ausgewertet wird, und auf das Ergebnis dann `c` addiert wird. Genau so kann man nicht nur ``obj.meth()`` schreiben, sondern auch ``obj.meth().meth2()`` wobei erst ``obj.meth()`` ausgewertet wird und auf dem Ergebnis dann `meth2()` aufgerufen wird.
BlackJack

Code: Alles auswählen

#!/usr/bin/env python
from itertools import chain, repeat


def main():
    with open('test.txt') as lines:
        with open('test2.txt', 'w') as out_file:
            out_file.writelines(
                chain.from_iterable(
                    repeat(value + '\n', int(count))
                    for value, count in (line.split(';') for line in lines)
                )
            )


if __name__ == '__main__':
    main()
:-)
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

oder:

Code: Alles auswählen

#!/usr/bin/env python
 
def main():
    with open('test.txt') as lines:
        with open('test2.txt', 'w') as out_file:
            out_file.writelines(
                (value + '\n') * int(count)
                    for value, count in (line.split(';') for line in lines)
            )
 
if __name__ == '__main__':
    main()
self.made_man
User
Beiträge: 7
Registriert: Mittwoch 11. Dezember 2013, 19:37

Kistenschieber wollte den Code nicht fertig bekommen, sondern verstehen. Hier vermute ich, wenn es so früh klemmt, sind ihm Listen Abstraktionen nicht bekannt, und er wundert sich, warum das "for" ohne Doppelpunkt auskommt.

http://www.python-kurs.eu/list_comprehension.php

Für einen Anfänger lässt sich das Programm aber durchaus auch mit "klassischem for" realisieren. Also Mut, es gibt mehrere Wege nach Rom.

@BlackJack: Danke für die Richtigstellung der Erklärung, Methode auf Methode anwenden ist in der Tat Quatsch. Es ist das erzeugte Objekt der ersten Methode, auf das die zweite Methode fußt.
self.made_man
User
Beiträge: 7
Registriert: Mittwoch 11. Dezember 2013, 19:37

Übrigens sieht man hier, wie das Konzept der Strukturierung mittels Einrückung auch bröckeln kann. Denn diese Form ist ebenfalls korrekt:

Code: Alles auswählen

#!/usr/bin/env python
 
def main():
    with open('test.txt') as lines:
        with open('test2.txt', 'w') as out_file:
            out_file.writelines(
          (value + '\n') * int(count)
      for value, count in (line.split(';') for line in lines)
                      )
 
if __name__ == '__main__':
    main()
Solange eine Zeile nicht zu Ende ist, sind die Einrückungen dem Python Interpreter egal. Er verkraftet da auch Murks wie hier gezeigt. Erst nach der schließenden Klammer in Zeile 9 ist die "Zeile" (die sich von 6 bis 9 erstreckt) fertig. Erst die Einrückung in Zeile 11 wird wieder ausgewertet.
BlackJack

@self.made_man: Zur verlinkten Seite: Als Ersatz für `reduce()` sehe ich „list comprehensions” überhaupt nicht. Und der Abschnitt „Anspruchsvolleres Beispiel” ist total gruselig.

Das ``locals()['_[1]']`` innerhalb einer „list comprehension” die Liste ergibt, die mit der LC gerade aufgebaut wird, ist ein Implementierungsdetail von CPython was *so* unsicher, undbekannt, und exotisch ist, dass man sich da niemals drauf verlassen sollte. Programme die sich darauf stützen sind schlicht kaputt. Es gibt ja Implementierungsdetails wo die Leute manchmal sagen „dann läuft das halt nur mit dieser Python-Implementierung”, aber das hier ist so weit weg von einer öffentlichen API dass ich nicht überrascht wäre, wenn die Python-Core-Entwickler das ändern ohne auch nur im geringsten damit zu rechnen, dass das irgendwelchen Python-Code da draussen tatsächlich beeinflussen könnte.

Beim Beispiel im letzten Abschnitt ist die Einrückung kaputt, schönes Beispiel warum man Leerzeichen und Tabulator-Zeichen nicht mischen sollte. Der Algorithmus ist auch ungewöhnlich. So ein Sieb kann man einfacher und effizienter umsetzen. Das ständige lineare Suchen nach Listenelementen ist ein unnötiger Leistungskiller.

Zur Einrückung: Mit Kommentaren kann man die ”Optik” auch sehr einfach stören und die Struktur des Programms unübersichtlicher gestalten.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Wieder was neues gelernt ;-) Allerdings ist mir bisher noch nie die gerade erzeugte Liste innerhalb einer Listcomprehension verwenden zu wollen. Das Beispiel nehme ich auch in die Top 3 der kompliziertesten Möglichkeiten Primzahlen zu finden auf. Hätten die Seitenschreiber ein »set« benutzt wäre es wohl nicht mal in den Top 10 gelandet.
BlackJack

@Sirius3: Ich dachte ich probiere mal aus wie das auf anderen Implementierungen als CPython aussieht und bin dann bei CPython selbst schon mit der Technik auf die Nase gefallen:

Code: Alles auswählen

$ python
Python 2.7.3 (default, Sep 26 2013, 20:08:41) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> [locals().copy() for _ in 'x']
[{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__doc__': None, '_': 'x', '__package__': None}]
>>> [locals()['_[1]'] for _ in 'x']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: '_[1]'
Das funktioniert also selbst in CPython nicht (mehr) so.

Edit: Mal unter die Haube geschaut sieht man, dass die Liste nur auf dem Stapel der VM liegt:

Code: Alles auswählen

>>> dis.dis(compile("[locals().copy() for _ in 'x']", '<test>', 'exec'))
  1           0 BUILD_LIST               0
              3 LOAD_CONST               0 ('x')
              6 GET_ITER            
        >>    7 FOR_ITER                21 (to 31)
             10 STORE_NAME               0 (_)
             13 LOAD_NAME                1 (locals)
             16 CALL_FUNCTION            0
             19 LOAD_ATTR                2 (copy)
             22 CALL_FUNCTION            0
             25 LIST_APPEND              2
             28 JUMP_ABSOLUTE            7
        >>   31 POP_TOP             
             32 LOAD_CONST               1 (None)
             35 RETURN_VALUE      
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

self.made_man hat geschrieben:Übrigens sieht man hier, wie das Konzept der Strukturierung mittels Einrückung auch bröckeln kann. Denn diese Form ist ebenfalls korrekt:
Hier bröckelt doch gar nichts. Nur weil man etwas nutzen kann, muss es noch lange nicht genutzt werden. Und hier besteht auch kein Grund, dass künstlich eine Einschränkung geschaffen wird. Es kann doch durchaus Fälle geben, in denen das sinnvoll ist und für mehr Übersicht sorgt. Im Normalfall greifst du doch auch nicht wild auf Implementierungsdetails zu, nutzt ausgibig globale Variablen oder ignorierst andere Regeln. Trotzdem könntest du es tun. In der Regel gewinnt dann aber doch die Vernunft.
Das Leben ist wie ein Tennisball.
kistenschieber
User
Beiträge: 2
Registriert: Donnerstag 12. Dezember 2013, 16:36

Hallo nochmal an alle im Forum,

zuerst mal sorry das ich mir mit der Antwort so viel Zeit gelassen habe.

Als nächstes ein herzliches Danke!

Und dann noch folgendes:

@BlackJack: Danke natürlich auch dir für deine Hilfe, aber eins muss ich dennoch loswerden:
Für einen Helicopterpiloten mag es offensichtlich sein wie man so ein Ding fliegt.
Für einen Herzchirurgen ist eine OP am offenen Herzen wahrscheinlich kein großes Thema.
Für Linus Torvalds ist der Linux Kernel wohl ein offenes Buch.
Für dich ist meine Vogehensweise offensichtlich falsch.

Für _mich_ hat die Lösung des Problems immer noch ein bischen mit Voodoo zu tun :-)

@self.made_man: Danke für den Link, ist gebookmarked. So sehr ich mich über die Lösung freue, um so mehr freue ich mich darauf den Lösungsweg verstehen zu lernen.

@Alle: Danke nochmals, ich werde die interessanten Diskussionen hier im Forum sicher weiter verfolgen.

Gruß

Kistenschieber
BlackJack

@kistenschieber: Das ist für *jeden* offensichtlich falsch denn es läuft ja nicht. Dazu muss man kein Python-Experte sein, sondern es einfach nur ausführen. Dann bekommt man in Zeile 6 einen ``TypeError: list indices must be integers, not str``. Was auch eine relativ deutliche Aussage ist. Es gibt wesentlich kryptischere Fehlermeldungen.
Antworten