Daten auslesen

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.
Eisi
User
Beiträge: 62
Registriert: Sonntag 24. November 2013, 21:59

Hallo zusammen,

Ich beschäftige mich im Moment mit lesen und schreiben von Daten wozu mir auch gleich ein kleines Projekt eingefallen ist.

Ich möchte mehrere Daten dessen Name immer gleich ist (z.B. Test.txt) in unterschiedlichen Ordner (dessen Name zum Teil anders ist) nach einem eingegebenen Wert durchsuchen. Wurde der Wert gefunden soll er ausgeben wo er ihn gefunden hat.

Die Ordnerstruktur ist so angeordnet:

Hauptordner > mehrere Ordner mit unterschiedlichen Projektnamen > öffnet man ein Projekt sind dort 6 unterordner die es in jedem Projekt gibt aber immer den gleichen Namen haben (test1, test2, test3 usw.) > in diesen Unterordner gibt es unterschiedliche .txt Daten die aber immer gleich heißen sowie auch die gesuchte Test.txt.

Da die Projekte aber logischerweise mit der Zeit zunehmen, muss das Programm automatisch immer alle vorhandenen Projektordner durchsuchen.

Ich hoffe ich habe es gut erklärt und ihr könnt mir Tipps geben wie und womit ich da am besten anfange.

MfG eisi
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@Eisi: schau Dir mal `os.walk` an.
BlackJack

@Eisi: Die übliche vorgehensweise ist das Problem rekursiv in immer kleinere Teilprobleme zu zerlegen. Und wenn ein Teilproblem so einfach ist, dass man es mit ein paar Zeilen Code lösen kann, dann tut man das und testet den Code. Wenn man alle Teilprobleme gelöst hat, die zusammen ein Teilproblem eine Ebene höher lösen, dann kann man Code schreiben der eben jenes Teilproblem löst, und das kann man dann testen. Bis man irgendwann eine funktionierende Gesamtlösung hat.

Ganz grob kann man das Programm anhand der Ordnerstruktur aufteilen:

Code: Alles auswählen

Programm:

    * Wert eingeben lassen.

    * Für jeden Projektordner im Hauptordner:

      * Für jeden der vorgegebenen Unterordner im Projekt:

        * Suche eingegebenen Wert in 'Test.txt'.
Wobei man dieses Gerüst ja schon fast 1:1 in Python-Code umsetzen könnte:

Code: Alles auswählen

def search_in_projects(root_path, needle):
    for project_path in iter_project_paths(root_path):
        for sub_directory in ['one', 'two', 'three']:
            search_in_file(
                needle, os.path.join(project_path, sub_directory, 'test.txt')
            )
`iter_project_paths()` und `search_in_file()` müsste man noch implementieren, damit das läuft. `search_in_file()` ist einfach, und kann direkt gelöst werden:

Code: Alles auswählen

def search_in_file(needle, filename):
    with open(filename, 'r') as lines:
        if any(needle in line for line in lines):
            print(filename)
Die Funktion kann man auch leicht testen.
Bei der `iter_projects_path()` kann man das Problem weiter aufteilen. Mit `os.listdir()` bekommt man alle Namen in einem Verzeichnis. Allerdings muss man noch testen ob es sich um ein Projektverzeichnis handelt oder nicht. Diesen Test kann man in eine eigene Funktion auslagern die einen Pfad auf diese Eigenschaft testet. Es handelt sich um ein Projektverzeichnis wenn der Pfad a) ein Verzeichnis beschreibt und b) die nötigen Unterordner vorhanden sind. Die stehen oben ja schon mal als Liste ind der `search_in_projects()`. Da Wiederholungen von Code und Daten schlecht sind, sollte man dafür eine Konstante einführen die an beiden Stellen im Quelltext verwendet wird. Der Test auf einen Projektpfad ist auch wieder einfach zu testen:

Code: Alles auswählen

PROJECT_SUB_DIRS = ['one', 'two', 'three']

# ...

def is_project_path(path):
    return os.path.isdir(path) and all(
        os.path.isdir(os.path.join(path, sub_directory))
        for sub_directory in PROJECT_SUB_DIRS
    )
Und damit lässt sich ziemlich trivial die `iter_project_paths()` implementieren, weil man mit Hilfe des Tests nur noch die passenden Namen aus der Verzeichnisauflistung herausfiltern muss:

Code: Alles auswählen

from itertools import ifilter

# ...

def iter_project_paths(root_path):
    return ifilter(is_project_path, os.listdir(root_path))
Auch das lässt sich wieder leicht testen in dem man es mal auf verschiedene Pfade loslässt die Projektordner enthalten oder auch nicht.

Fehlt noch das Hauptprogramm wo der Benutzer nach einem Wert gefragt wird:

Code: Alles auswählen

def main():
    needle = raw_input('Suchwert: ')
    search_in_projects('/home/user/projects', needle)
Zusammen ergibt das (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python
from __future__ import print_function
import os
from itertools import ifilter

PROJECT_SUB_DIRS = ['one', 'two', 'three']


def search_in_file(needle, filename):
    with open(filename, 'r') as lines:
        if any(needle in line for line in lines):
            print(filename)


def is_project_path(path):
    return os.path.isdir(path) and all(
        os.path.isdir(os.path.join(path, sub_directory))
        for sub_directory in PROJECT_SUB_DIRS
    )


def iter_project_paths(root_path):
    return ifilter(is_project_path, os.listdir(root_path))


def search_in_projects(root_path, needle):
    for project_path in iter_project_paths(root_path):
        for sub_directory in PROJECT_SUB_DIRS:
            search_in_file(
                needle, os.path.join(project_path, sub_directory, 'test.txt')
            )


def main():
    needle = raw_input('Suchwert: ')
    search_in_projects('/home/user/projects', needle)


if __name__ == '__main__':
    main()
Eisi
User
Beiträge: 62
Registriert: Sonntag 24. November 2013, 21:59

Erst einmal danke für die ausführliche Antwort. Da ich Version 3.4 in Python habe musste ich das ein oder andere anpassen.

Code: Alles auswählen

from itertools import ifilter
wurde zu

Code: Alles auswählen

from itertools import filter
und trotzdem sagt er mir ständig das er es nicht laden konnte. Was mache ich falsch?
Eisi
User
Beiträge: 62
Registriert: Sonntag 24. November 2013, 21:59

Wenn ich ein * bei import filter einsetze gibt er keinen fehler mehr aus. Aber auch kein Ergebnis.

Code: Alles auswählen

from __future__ import print_function
import os
from itertools import *


PROJECT_SUB_DIRS = ['ordner1', 'ordner2', 'ordner3', 'ordner4', 'ordner5', 'ordner6']

def search_in_file(needle, filename):
    with open(filename, 'r') as lines:
        if any(needle in line for line in lines):
            print(filename)


def is_project_path(path):
    return os.path.isdir(path) and all(
        os.path.isdir(os.path.join(path, sub_directory))
        for sub_directory in PROJECT_SUB_DIRS
    )

def iter_project_paths(root_path):
    return filter(is_project_path, os.listdir(root_path))


def search_in_projects(root_path, needle):
    for project_path in iter_project_paths(root_path):
        for sub_directory in PROJECT_SUB_DIRS:
            search_in_file(
                needle, os.path.join(project_path, sub_directory, 'test.txt')
            )


def main():
    needle = input('Nummer: ')
    search_in_projects('C:\\Users\\Christoph\\Desktop\\python\\test', needle)


if __name__ == '__main__':
    main()
BlackJack

@Eisi: Der ``import`` insgesamt ist falsch. Die eingebaute Funktion `filter()` ist in Python 3 das was in 2 `itertools.ifilter()` ist. Also auch ein *-Import macht keinen Sinn, lass das also besser.

Wenn das nicht funktioniert, dann müsstest Du jetzt einfach mal die Funktionen einzeln durchtesten wo der oder die Fehler liegen. Wie gesagt, ich hatte das gar nicht getestet, aber die Funktionen sind ja so geschrieben, dass man sie einfach testen kann.
Eisi
User
Beiträge: 62
Registriert: Sonntag 24. November 2013, 21:59

Habe alle Funktionen einzeln getestet. Alles klappt prima bis auf

Code: Alles auswählen

def search_in_projects(root_path, needle):
    for project_path in iter_project_paths(root_path):
        for sub_directory in PROJECT_SUB_DIRS:
            search_in_file(
                needle, os.path.join(project_path, sub_directory, '.kd')
            )
Er gibt halt keinen Fehler aber auch kein Ergebnis aus. Da Muttertag ist muss ich das Denken jetzt leider unterbrechen und bin später wieder dran.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Eisi hat geschrieben: Er gibt halt keinen Fehler aber auch kein Ergebnis aus. Da Muttertag ist muss ich das Denken jetzt leider unterbrechen und bin später wieder dran.
Und wie *genau* lautet der Fehler? Mit der Info alleine können wir wenig anfangen.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Eisi
User
Beiträge: 62
Registriert: Sonntag 24. November 2013, 21:59

Es kommt einfach nichts. Ich gebe den Wert ein und dann passiert nichts. Kein Fehler und kein Ergebnis.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Dann lass dir doch mal die Zwischenschritte und Zwischenergebnisse ausgeben.
Das Leben ist wie ein Tennisball.
Eisi
User
Beiträge: 62
Registriert: Sonntag 24. November 2013, 21:59

Kann es sein das in dem Code irgendwo versteckt noch python 2.7 code ist? Da ich ja 3.4 verwende könnte ich mir so etwas änliches vorstellen. Das testen der Zwischenschritte bringt mir auch keinen Fehler.
Beim Filter kommt <filter object at 0x02DFBAD0> so etwas raus. Ist es vielleicht möglich das die Ausgabe noch umgewandelt werden muss?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ähem... da kann ja nicht *der* einige Zwischenschritt sein! Da sind doch zig for-Schleifen drin...

Du kannst Iterator- (oder auch Generator-) Ausdrücke immer mittels ``list()`` in Listen wandeln. Damit siehst Du dann deren Inhalt.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@Eisi: Ja, die Funktion gibt einen Iterator zurück. Wenn Du Dir die Elemente anschauen möchtest, dann musst Du da drüber iterieren und sie ausgeben, oder mit `list()` die Elemente in einer Liste ”auffangen” und die Liste ausgeben.
Eisi
User
Beiträge: 62
Registriert: Sonntag 24. November 2013, 21:59

Ich kann den Fehler einfach nicht finden :cry:
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

An welchen Stellen hast Du Dir denn Zwischenschritte eingebaut und an welcher Stelle kannst Du das Verhalten nicht mehr nachvollziehen? Da muss Dir doch auffallen, dass z.B. eine Liste komplett leer ist, in der aber etwas stehen sollte...
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Eisi
User
Beiträge: 62
Registriert: Sonntag 24. November 2013, 21:59

So nochmal ein wenig rum probiert und musste feststellen, dass wenn ich

Code: Alles auswählen

def iter_project_paths(root_path):
    return filter(is_project_path, os.listdir(root_path))
teste und als liste mit print ausgebe nur "[]" erscheint. Ich denke das wird das problem verursachen
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Das kann eigentlich nur zwei Ursachen haben:

1. Du übergibst als ``root_path`` nicht den tatsächlichen Projektordner - also den Pfad, dessen Unterverzeichnisse die eigentlichen Projekte sind.

2. In ``PROJECT_SUB_DIRS`` sind nicht die Ordernamen deiner Projekte enthalten (hier *keine* vollständige Pfadangabe, sondern wirklich nur den Namen des einzelnen Ordners).

Hilfreich zum Debugging wären natürlich entsprechende ``print()``-Ausgaben und eine detailliertere Beschäftigung mit dem vorgesetzten Code, damit man wenigstens ansatzweise versteht, was da eigentlich passiert.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Mir fällt gerade eine Schwäche an BlackJacks Code auf: ``is_project_path()`` testet ``os.path.isdir(path)``, bekommt aber jeweils einen Eintrag vom ``os.listdir()``-Aufruf. Letzteres liefert aber keine absoluten Pfadangaben, sondern nur die Dateinamen. Folglich schlägt der Test fehl, sobald der übergebene ``root_path`` nicht dem Verzeichnis entspricht, von wo aus das Skript gestartet wurde. Hiermit revidiere ich übrigens die zuvor gemachte (implizite) Feststellung, dass der Fehler nur beim Benutzer liegen kann. :)
Eisi
User
Beiträge: 62
Registriert: Sonntag 24. November 2013, 21:59

snafu hat geschrieben: 2. In ``PROJECT_SUB_DIRS`` sind nicht die Ordernamen deiner Projekte enthalten (hier *keine* vollständige Pfadangabe, sondern wirklich nur den Namen des einzelnen Ordners).
Nein in PROJECT_SUB_DIRS (sollten) sind die Unterverzeichnisse der Projekte die ja immer den gleichen Namen enthalten. Der Name der sich ändert ist ja nur der Projektname selber.

Pfade sind überprüft und auch die Namen der Unterordner sind soweit korrekt.

root_path = 'C:\\Users\\Christoph\\Desktop\\python\\test'

Der Pfad zum Projekt(Dessen Name unvorhersehbar ist) sieht dann z.B. so aus: C:\\Users\\Christoph\\Desktop\\python\\test\\Projekt1

wo sich dann die 6 Unterordner befinden die immer den gleichen namen haben.z.B.
C:\\Users\\Christoph\\Desktop\\python\\test\\Projekt1\\Unterordner1
C:\\Users\\Christoph\\Desktop\\python\\test\\Projekt1\\Unterordner2
C:\\Users\\Christoph\\Desktop\\python\\test\\Projekt1\\Unterordner3
C:\\Users\\Christoph\\Desktop\\python\\test\\Projekt1\\Unterordner4
C:\\Users\\Christoph\\Desktop\\python\\test\\Projekt1\\Unterordner5
C:\\Users\\Christoph\\Desktop\\python\\test\\Projekt1\\Unterordner6

In jedem dieser 6 Ordner befindet sich dann die Datei die immer gleich heißt

C:\\Users\\Christoph\\Desktop\\python\\test\\Projekt1\\Unterordner1\\test.txt

Nur um das nochmal zu veranschaulichen. Nicht das ich am anfang irgendwo falsch verstanden wurde. :roll:
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Wenn man die betreffende Funktion mit der folgenden ersetzt, dann müsste es funktionieren:

Code: Alles auswählen

def iter_project_paths(root_path):
    return (
        is_project_path(os.path.join(root_path, sub_dir))
        for sub_dir in os.listdir(PROJECT_SUB_DIRS)
    )
EDIT: Korrektur...

Code: Alles auswählen

def iter_project_paths(root_path):
    dirnames = (
        os.path.join(root_path, sub_dir)
        for sub_dir in os.listdir(PROJECT_SUB_DIRS)
    )
    return filter(is_project_path, dirnames)
Zuletzt geändert von snafu am Montag 12. Mai 2014, 15:13, insgesamt 2-mal geändert.
Antworten