FlatTree vom Dateisystem

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.
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Ich hatte letztens schonmal die Diskussion über die Erstellung eines Baums. Mein Vorschlag dazu war es, die Baumstruktur flach zu halten und alles in der Art einer Tabelle wie hier abzubilden:

Code: Alles auswählen

Pfad: /foo/bar

 id | name | parent
----+------+--------
  1 | /    | None
  2 | foo  | 1
  3 | bar  | 2
Nachdem ich gründlich nachgedacht habe, bin ich zu dem Entschluss gekommen, dass ich diese flache Struktur aufgrund vieler Vorteile verwenden möchte. Mein Ziel ist es, das Dateisystem (oder einen Teil davon) in diese flache Form zu bringen und in einer XML-Datei zu speichern, ungefähr so:

Code: Alles auswählen

<?xml version="1.0"?>
<filesystem>
	<directories>
		<directory ID="1" name="/" />
		<directory ID="2" name="foo" parentID="1" />
	</directories>
	<files>
		<file ID="3" name="bar" parentID="2" size="21435" />
	</files>
</filesystem>
Nun zu den Fragen:

1) Wenn es sich um sehr viele Verzeichnisse/Dateien handelt, wird der von mir getaufte `FlatTree` natürlich sehr groß. Während jedes Verzeichnis rekursiv durchwandert wird, werden ID, Name und parentID in einem Dict festgehalten. Wieviel Datenmenge kann Python denn eigentlich handeln? Ist das vom Arbeitsspeicher abhängig oder wodurch wird begrenzt, wieviel Inhalt ich in eine Variable packen kann?

2) Weiß jemand, wieviele Dateien maximal in einem Verzeichnis liegen dürfen? Ist das vom Dateisystem abhängig oder vom Betriebssystem oder... ? Beim Brennen von CDs kann man beispielsweise die ISO-Restriktionen lockern, um mehr als 8 Unterverzeichnisse zu ermöglichen. Existieren solche Beschränkungen auch für die maximale Anzahl der Dateien in einem Verzeichnis?
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

droptix hat geschrieben:1) Wenn es sich um sehr viele Verzeichnisse/Dateien handelt, wird der von mir getaufte `FlatTree` natürlich sehr groß. Während jedes Verzeichnis rekursiv durchwandert wird, werden ID, Name und parentID in einem Dict festgehalten. Wieviel Datenmenge kann Python denn eigentlich handeln? Ist das vom Arbeitsspeicher abhängig oder wodurch wird begrenzt, wieviel Inhalt ich in eine Variable packen kann?
Auf den meisten modernen Betriebssystemen bekommt jeder Prozess seinen eigenen Virtuellen Speicher, auf 32-Bit Systemen 4 Gigabyte (wenn ich mich richtig erinnere, es können aber auch nur 2 Gigabyte sein). Diese kann Python wie jeder andere Prozess vollständig nutzen. Jedoch sieht es in der Realität so aus, dass der verfügbare RAM ein Limit setzt, da ansonsten Swap benötigt wird, der eher langsam ist.
droptix hat geschrieben:2) Weiß jemand, wieviele Dateien maximal in einem Verzeichnis liegen dürfen? Ist das vom Dateisystem abhängig oder vom Betriebssystem oder... ? Beim Brennen von CDs kann man beispielsweise die ISO-Restriktionen lockern, um mehr als 8 Unterverzeichnisse zu ermöglichen. Existieren solche Beschränkungen auch für die maximale Anzahl der Dateien in einem Verzeichnis?
Die Anzahl der Dateien ist vom Dateisystem bedingt, so ermöglicht NTFS 2**32-1, ReiserFS 2**32-3 und ext3 Volumengröße_in_Bytes/(2**13) bei Standardeinstellungen.
Aber in diesen Dateisystem existiert meines Wissens kein Limit auf die Verteilung der Dateien. So spricht nichts dagegen, alle Inodes in einem Ordner aufzubrauchen. Eine Ausnahme macht ReiserFS, mit 1.200.000 Dateien pro Verzeichnis, danach kann es sein, dass dessen Hashfunktion Kollisionen liefert.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

Falls bei dem Programm wirklich mehr Dateien verwaltet werden müssen als in den Speicher passen, würde ich für die flache Datenstruktur eine Datenbank benutzen.

Oder wenn man das Dateisystem mit Objekten in einer Baumstruktur abbildet, könnte man auch ZODB nehmen.

Bei XML muss man auch aufpassen, dass die Namen unter den meisten Unix-Dateisystemen im Grunde kein Text, sondern Binärdaten sind. Bis auf das Nullbyte und '/' ist in der Regel alles erlaubt. Nichtdruckbare Zeichen mit ASCII-Code kleiner 32 sind aber teilweise illegal in XML-Dokumenten!
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Ich habe mir gedacht, dass das ganz witzig zu implementieren wäre und daher Paste #482 erstellt. Es ist eigentlich ganz ok, bis auf das gehacke mit basenames ist es eigentlich ein guter Ausgangspunkt für eigene Entwicklungen.

Schade nur, dass die Version 1.2.6 kein Prettyprinting out-of-the-box unterstützt. Das selbst zu implementieren war mir zu oberflächlich ;) Ebenso habe ich die die XML-Versionszeile ausgelassen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

Gerade wenn man erwartet, dass der Umfang der Daten eventuell den Hauptspeicher sprengt, sollte man das XML nicht komplett im Speicher erstellen oder parsen. Das bläht alles zusätzlich um ein vielfaches auf. Wenn man die Objektstruktur hat, lässt sich das XML aber relativ einfach seriell erzeugen und gleich in eine Datei schreiben.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Leonidas hat geschrieben: Auf den meisten modernen Betriebssystemen bekommt jeder Prozess seinen eigenen Virtuellen Speicher, auf 32-Bit Systemen 4 Gigabyte (wenn ich mich richtig erinnere, es können aber auch nur 2 Gigabyte sein).
Bei Windows XP 32BIT leider nur 2GB Physikalischen Arbeitsspeicher für ein Prozess, auch wenn z.B. 4GB RAM vorhanden sind :( Wenn das überschritten wird muss halt geswapt werden bzw. geswapt wird eigentlich ehe die ganze Zeit @Win ^^

lg
BlackJack

sape hat geschrieben:Bei Windows XP 32BIT leider nur 2GB Physikalischen Arbeitsspeicher für ein Prozess, auch wenn z.B. 4GB RAM vorhanden sind :( Wenn das überschritten wird muss halt geswapt werden
Wenn die 2 GiB *virtueller* Arbeitsspeicher für einen Prozess erreicht sind, dann ist Ende und Python steigt mit einem `MemoryError` aus. Swappen wird nötig wenn das physikalische RAM voll ist. Windows lagert aber auch vorher schon regelmässig Speicherseiten aus.

Man kann den virtuellen Adressraum für einen Benutzerprozess auf 32-Bit-Systemen auch auf 3 GiB erhöhen.
BlackJack

Habe auch mal etwas zusammengehackt:

http://paste.pocoo.org/show/485/

Es wird eine Baum aus Objekten aufgebaut. Bei `dump()` kann man sehen, wie man daraus mit rekursiven Aufrufen einen Baum ausgibt und in der `main()` ist Quelltext, der die Objekte in der Form: Typ, ID, "Eltern"-ID und Name als Tabelle ausgibt.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Leonidas hat geschrieben:Ich habe mir gedacht, dass das ganz witzig zu implementieren wäre und daher...
Da sieht man es wieder --> mit Python programmieren macht Spaß. :mrgreen:

lg
Gerold
:-)

PS: Sonnenschein und Vogelgezwitscher -- die größten Feinde eines Programmierers. :P
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

BlackJack hat geschrieben:Falls bei dem Programm wirklich mehr Dateien verwaltet werden müssen als in den Speicher passen, würde ich für die flache Datenstruktur eine Datenbank benutzen.
Der Aufwand würde den Nutzen denke ich nicht rechtfertigen. Man könnte hier für eine schnelle Lösung zwar auf SQLite zurück greifen, aber es ist schon umständlich, erst alles in einer DB zu erfassen, um daraus dann das XML zu erzeugen. Ich mach das erstmal in einem Dictionary und werde mal die Grenzen austesten.

Irgendjemand gab letztens den Tipp, dass SQLite in Python 2.5 mittlerweile auch DBs im Arbeitsspeicher erstellen kann, so dass dafür keine Datei angelegt werden muss. Würde sich dafür perfekt eignen, aber:

1) Was passiert, wenn der Arbeitsspeicher voll ist? Swapping?

2) Kann man das neue SQLite auch für ältere Python-Versionen nachrüsten?
BlackJack hat geschrieben:Bei XML muss man auch aufpassen, dass die Namen unter den meisten Unix-Dateisystemen im Grunde kein Text, sondern Binärdaten sind. Bis auf das Nullbyte und '/' ist in der Regel alles erlaubt. Nichtdruckbare Zeichen mit ASCII-Code kleiner 32 sind aber teilweise illegal in XML-Dokumenten!
Wie jetzt? Unter Unix sind Dateinamen doch auch Text. Ich kenne keine Dateinamen, die Zeichen unter ASCII-Code 32 enthalten. Gibt's dafür mal ein Beispiel? Ich würde wegen Sonderzeichen natürlich mit UTF-8 und Entities arbeiten. Können Dateinamen überhaupt spitze Klammern und sowas enthalten? Unter Windows ist das glaube ich nicht zulässig. Ich kann dann mal versuchen, mittels Hilfsprogrammen unter Windows Dateien mit komischen Namen anzulegen...
BlackJack hat geschrieben:Gerade wenn man erwartet, dass der Umfang der Daten eventuell den Hauptspeicher sprengt, sollte man das XML nicht komplett im Speicher erstellen oder parsen. Das bläht alles zusätzlich um ein vielfaches auf. Wenn man die Objektstruktur hat, lässt sich das XML aber relativ einfach seriell erzeugen und gleich in eine Datei schreiben.
Mit seriell meinst du ich generiere XML-Code und hänge den mittels writeline() an eine Datei hinten ran? Dann laufe ich aber Gefahr, nicht wohlgeformtes XML zu erzeugen... wenn z.B. das End-Tag vom Wurzelelement fehlt, weil ein Memory-Error zwischendrin abgebrochen hat. Ich muss das XML-Dokument zur Weiterverarbeitung ja auch wieder in Python einlesen können! Also irgendwie muss das in den Speicher passen :roll:
BlackJack hat geschrieben:Wenn die 2 GiB *virtueller* Arbeitsspeicher für einen Prozess erreicht sind, dann ist Ende und Python steigt mit einem `MemoryError` aus. Swappen wird nötig wenn das physikalische RAM voll ist.
Nunja, es wäre ja dann Sache des Betriebssystems, hier durch Swapping für ausreichend Speicher zu sorgen. Ist mir dann erstmal egal, ob das langsamer ist, hauptsache es geht.
BlackJack

droptix hat geschrieben:
BlackJack hat geschrieben:Falls bei dem Programm wirklich mehr Dateien verwaltet werden müssen als in den Speicher passen, würde ich für die flache Datenstruktur eine Datenbank benutzen.
Der Aufwand würde den Nutzen denke ich nicht rechtfertigen. Man könnte hier für eine schnelle Lösung zwar auf SQLite zurück greifen, aber es ist schon umständlich, erst alles in einer DB zu erfassen, um daraus dann das XML zu erzeugen. Ich mach das erstmal in einem Dictionary und werde mal die Grenzen austesten.
So aufwändig muss das ja nicht werden. Das erst einmal mit den eingebauten Datentypen zu machen ist keine schlechte Idee. Und den Datenbankzugriff kann man dann später auch so gestalten, dass sich für den Code, der vorher ein Dictionary benutzt hat, nichts ändert. Dann wäre es austauschbar. Man könnte dann auch zuerst ein Dictionary benutzen, und erst wenn das eine bestimmte Grösse erreicht hat, auf die Datenbank umsteigen.

Statt SQLite kann man auch erst einmal `shelve` benutzen. Das verhält sich ja schon wie ein Dictionary.
Irgendjemand gab letztens den Tipp, dass SQLite in Python 2.5 mittlerweile auch DBs im Arbeitsspeicher erstellen kann, so dass dafür keine Datei angelegt werden muss. Würde sich dafür perfekt eignen, aber:

1) Was passiert, wenn der Arbeitsspeicher voll ist? Swapping?
Natürlich was sonst. Aber wie kommst Du jetzt überhaupt darauf? Es ging ja gerade darum eine Datenbank zu benutzen wenn die Daten nicht mehr komplett in den Speicher passen. Wenn man diese DB dann im Speicher anlegt, wird sie ganz bestimmt nicht in den Speicher passen und alles wird noch langsamer weil noch die unnötige Datenbankschicht dazwischen liegt.

Der Sinn der Datenbank ist in diesem Fall doch gerade, dass man den Hauptspeicher entlastet, aber trotzdem noch möglichst schnell und effizient auf die Daten zugreifen kann.
2) Kann man das neue SQLite auch für ältere Python-Versionen nachrüsten?
Diese Funktion ist soweit ich weiss nicht neu. Man muss als DB-Namen nur ':memory:' angeben.
BlackJack hat geschrieben:Bei XML muss man auch aufpassen, dass die Namen unter den meisten Unix-Dateisystemen im Grunde kein Text, sondern Binärdaten sind. Bis auf das Nullbyte und '/' ist in der Regel alles erlaubt. Nichtdruckbare Zeichen mit ASCII-Code kleiner 32 sind aber teilweise illegal in XML-Dokumenten!
Wie jetzt? Unter Unix sind Dateinamen doch auch Text. Ich kenne keine Dateinamen, die Zeichen unter ASCII-Code 32 enthalten. Gibt's dafür mal ein Beispiel?
Also ich habe mehrere PDF-Dokumente auf der Festplatte wo ein '\n' im Namen ist, weil die automatisch nach den Metadaten benannt wurden und der Titel einen Zeilenumbruch enthält.

Ansonsten:

Code: Alles auswählen

In [29]: name = ''.join(chr(i) for i in xrange(1, 256)).replace('/', '')

In [30]: f = open(name, 'w')

In [31]: f.write('test\n')

In [32]: f.close()

In [33]: ls
??????????????????????????????? !"#$%&'()*+,-.0123456789:;<=>?@ABCDEFGHIJKLMNOPQ
RSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~???????????????????????????????????
????????????????????????????????????????????????????????????????????????????????
??????????????

In [34]: ls --quoting-style=escape
\001\002\003\004\005\006\a\b\t\n\v\f\r\016\017\020\021\022\023\024\025\026\027\0
30\031\032\033\034\035\036\037\ !"#$%&'()*+,-.0123456789:;<\=\>?\@ABCDEFGHIJKLMN
OPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{\|}~\177\200\201\202\203\204\205\2
06\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\2
32\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\2
56\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\3
02\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\3
26\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\3
52\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\3
76\377

In [35]: cat *
test
Wie gesagt: alles ausser '/' und '\0' ist grundsätzlich erst einmal erlaubt. Die C Bibliotheksfunktionen erwarten einen Zeiger auf ein `char`-Array dessen Ende mit '\0' gekennzeichnet ist und das Betriebssystem interpretiert den '/' als Pfadtrenner. Alles andere wird bei vielen Dateisystemen einfach durchgereicht und auf die Platte geschrieben. In diesem Fall ist's ReiserFS gewesen.
Ich würde wegen Sonderzeichen natürlich mit UTF-8 und Entities arbeiten.
Wie willst Du von den Bytes zu UTF-8 kommen? Wenn ein Name das Byte '\xf6' enthält, ist das ein u'ö' oder ein u'÷'?
Mit seriell meinst du ich generiere XML-Code und hänge den mittels writeline() an eine Datei hinten ran? Dann laufe ich aber Gefahr, nicht wohlgeformtes XML zu erzeugen... wenn z.B. das End-Tag vom Wurzelelement fehlt, weil ein Memory-Error zwischendrin abgebrochen hat.
Die Gefahr besteht auch, wenn Du das XML erst komplett im Speicher generierst und dann erst rausschreibst.

Ich dachte auch eher an den `XMLWriter` aus `elementtree.SimpleXMLWriter` oder das man sich selber so eine Klasse schreibt, statt `writeline()` direkt zu benutzen.
Ich muss das XML-Dokument zur Weiterverarbeitung ja auch wieder in Python einlesen können! Also irgendwie muss das in den Speicher passen :roll:
Nein, auch einlesen kann man das wieder inkrementell, z.B. mit SAX oder `iterparse()` aus `elementtree.ElementTree`.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

droptix hat geschrieben:Mein Ziel ist es, das Dateisystem (oder einen Teil davon) in diese flache Form zu bringen und in einer XML-Datei zu speichern,
Hi droptix!

Mir persönlich gefällt es nicht, dieses aufgeblähte XML für so etwas heranzuziehen. Deshalb habe ich mir Gedanken über eine reine Shelve-Lösung gemacht. Allerdings gefällt mir mein Prototyp auch noch nicht so recht. Außerdem wird diese Lösung beim Abrufen aller Directories immer langsamer, je mehr Daten dazu kommen. Bei rießigen Systemen empfehle ich auf jeden Fall eine Datenbank mit vernünftigen Indizes zu verwenden.

Wie auch immer... :? Ich mach jetzt nicht weiter und zeige einfach mal was ich so beim Testen fabriziert habe, damit es nicht verloren geht:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-

import shelve
from pickle import HIGHEST_PROTOCOL
import os

FILENAME = "diritems.shelve"
TEMP_DIRS_FILENAME = "dirs.shelve"


class Item(object):
    
    def __init__(self, name, parent_id, is_dir):
        self.name = name
        self.parent_id = parent_id
        self.is_dir = is_dir


class DirItems(object):
    
    def __init__(self, filename = FILENAME):
        
        self._items = shelve.open(filename, protocol = HIGHEST_PROTOCOL)
    

    def add_item(self, id, name, parent_id, is_dir):
        id = str(id)
        item = Item(name, parent_id, is_dir)
        self._items[id] = item
    
    
    def get_item(self, id):
        return self._items[id]
    
    
    def get_item_full_path(self, id):
        id = str(id)
        names = []
        
        while True:
            item = self._items[id]
            names.insert(0, item.name)
            parent_id = item.parent_id
            if parent_id is None:
                break
            else:
                id = parent_id
        
        return os.path.join(os.sep, *names)
    
    
    def get_item_values(self, id):
        item = self._items[id]
        return (item.name, item.parent_id)
    
    
    def get_dirs_iterator(self):
        if os.path.isfile(TEMP_DIRS_FILENAME):
            os.remove(TEMP_DIRS_FILENAME)
        
        dirs = shelve.open(TEMP_DIRS_FILENAME, protocol = HIGHEST_PROTOCOL)
        
        for key, value in self._items.iteritems():
            if value.is_dir:
                dirs[key] = value
        
        for key, value in dirs.iteritems():
            yield (key, value)
        
        dirs.close()
        os.remove(TEMP_DIRS_FILENAME)
    
    
    def get_iteritems(self):
        return self._items.iteritems()
    

def main():
    
    # Testen
    
    diritems = DirItems()
    
    for i in range(1, 10 + 1):
        if i == 1:
            parent_id = None
        else:
            parent_id = str(i - 1)
        diritems.add_item(i, "dirname_" + str(i), parent_id, True)
    for i in range(21, 30 + 1):
        diritems.add_item(i, "filename_" + str(i), str(i - 20), False)
    
    # Alle Anzeigen
    for key, item in diritems.get_iteritems():
        print key, item.name, item.parent_id, item.is_dir
    print
    
    # Alle Directories
    for key, item in diritems.get_dirs_iterator():
        print key, item.name, item.parent_id, item.is_dir
    print
    
    # Full Pathname
    print diritems.get_item_full_path(30)
    

if __name__ == "__main__":
    main() 
mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

BlackJack hat geschrieben:Statt SQLite kann man auch erst einmal `shelve` benutzen. Das verhält sich ja schon wie ein Dictionary.
Was ist ein `shelve`? Ein sog. "persistentes Dictionary". Also ein Datentyp, wo quasi die Verbindung stets aufrecht erhalten wird, um irgendwie schneller drauf zugreifen zu können? Ich kenne das Wort "persistent" nur aus der DB-Welt.
BlackJack hat geschrieben:Es ging ja gerade darum eine Datenbank zu benutzen wenn die Daten nicht mehr komplett in den Speicher passen. […] Der Sinn der Datenbank ist in diesem Fall doch gerade, dass man den Hauptspeicher entlastet, aber trotzdem noch möglichst schnell und effizient auf die Daten zugreifen kann.
Da hast du wohl eindeutig Recht!
BlackJack hat geschrieben:Also ich habe mehrere PDF-Dokumente auf der Festplatte wo ein '\n' im Namen ist, weil die automatisch nach den Metadaten benannt wurden und der Titel einen Zeilenumbruch enthält. […] Wie gesagt: alles ausser '/' und '\0' ist grundsätzlich erst einmal erlaubt. Die C Bibliotheksfunktionen erwarten einen Zeiger auf ein `char`-Array dessen Ende mit '\0' gekennzeichnet ist und das Betriebssystem interpretiert den '/' als Pfadtrenner. Alles andere wird bei vielen Dateisystemen einfach durchgereicht und auf die Platte geschrieben.
Das ist echt krank :twisted: Aber gut zu wissen! Nun ist der Pfadtrenner (os.path.sep) unter Unixen ja üblicherweise ein Slash und unter Windows ein Backslash. Kann man unter Unix einen Backslash im Dateinamen benutzen und unter Windows einen Slash?
BlackJack hat geschrieben:Wie willst Du von den Bytes zu UTF-8 kommen? Wenn ein Name das Byte '\xf6' enthält, ist das ein u'ö' oder ein u'÷'?
Dann könnte man alternativ noch den Dateinamen in Byte-Strings umwandeln und die Zeichen '\xf6' etc. speichern... was den Sinn XML zu nutzen völlig in Frage stellt, weil der Inhalt nunmal auch für den Menschen lesbar sein soll.
BlackJack hat geschrieben:Ich dachte auch eher an den `XMLWriter` aus `elementtree.SimpleXMLWriter` oder das man sich selber so eine Klasse schreibt, statt `writeline()` direkt zu benutzen.
Klingt doch schon wonach ich suche. Cool!
BlackJack

droptix hat geschrieben:Was ist ein `shelve`? Ein sog. "persistentes Dictionary". Also ein Datentyp, wo quasi die Verbindung stets aufrecht erhalten wird, um irgendwie schneller drauf zugreifen zu können? Ich kenne das Wort "persistent" nur aus der DB-Welt.
Ein `shelve` ist im Grunde eine Art Dictionary bei dem nur Zeichenketten als Schlüssel erlaubt sind und alles was sich mit `pickle` serialisieren lässt als Werte. Die Daten werden in eine Datenbank geschrieben. Dafür wird das beste genommen was `anydbm` zu bieten hat. Also im günstigsten Fall die BerkleyDB, wenn sie auf dem System vorhanden ist.
Nun ist der Pfadtrenner (os.path.sep) unter Unixen ja üblicherweise ein Slash und unter Windows ein Backslash. Kann man unter Unix einen Backslash im Dateinamen benutzen und unter Windows einen Slash?
Die Windows-API nimmt auch ziemlich konsistent einen Slash als Pfadtrenner an.

Das man den Backslash unter Linux als Zeichen im Dateinamen haben kann, zeigt mein Beispiel. Da ist im Namen doch wirklich alles drin ausgenommen Nullbyte und '/'.
BlackJack hat geschrieben:Wie willst Du von den Bytes zu UTF-8 kommen? Wenn ein Name das Byte '\xf6' enthält, ist das ein u'ö' oder ein u'÷'?
Dann könnte man alternativ noch den Dateinamen in Byte-Strings umwandeln und die Zeichen '\xf6' etc. speichern... was den Sinn XML zu nutzen völlig in Frage stellt, weil der Inhalt nunmal auch für den Menschen lesbar sein soll.
XML soll in erster Linie maschinenlesbar sein. Für Menschen lesbar sind nur recht wenige XML-Applikationen. Dein Format gehört eher nicht dazu. Unter lesen verstehe ich jetzt, dass man auch etwas damit anfangen kann wenn man es als Mensch liest. Stell Dir mal 10000 Dateien in Deinem flachen Format vor und ob sich ein Mensch in so einer Datei zurechtfinden würde.

Selbst XHTML ist nur bedingt "menschenlesbar". Sowie eine etwas komplexere Tabelle, MathML oder SVG eingebettet sind oder ausgiebig von <div> und <span> für CSS Gebrauch gemacht wird, hört der Spass auf. Dann braucht man schon technische Hilfsmittel die über einen einfachen Texteditor hinausgehen.
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Nunja, XML eignet sich aber wunderbar für den plattformübergreifenden Datenaustausch. Daher fände ich es sehr nett, Teile des Dateisystems auf XML abbilden zu können.

Mal angenommen man pickle'd das Ganze -> also z.B. deine Idee mit PathElement-Klasse und einem set() als Baum. Kann man die Datei dann nur wieder in Python de-picklen?
BlackJack

Ja, "pickles" sind erstmal ziemlich Python-spezifisch. Es spricht ja auch nichts gegen XML als Format, man muss halt nur aufpassen das man Binärdaten entsprechend kodiert.
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

BlackJack hat geschrieben:man muss halt nur aufpassen das man Binärdaten entsprechend kodiert
Hast du vielleicht noch einen Tipp, wie man das vernünftig umsetzen könnte? Auch alle lesbaren Zeichen als Binär-Strings zu schreiben halte ich für unsinnig. In den wenigsten Fällen haben Dateien solche 'verkrüppelten' Namen. Aber auch diese Fälle möchte ich berücksichtigen.

Daher sollte man den Dateinamen vielleicht vorher auf Binärzeichen untersuchen. Wenn er aus solchen besteht, konvertiert man den gesamten Namen, ansonsten bleiben es Zeichen aus dem UTF-8 Zeichensatz. Im XML-Code steht dann eben:

<file name="\xf4\xf5\xf6" binary="yes" />

Ne Idee?
BlackJack

Eine gute Standardkodierung wäre wohl "Quoted Printable". Das wird aus ähnlichen Gründen bei E-Mail verwendet. Ist in Python auch sehr einfach umsetzbar:

Code: Alles auswählen

In [56]: '\001\n\b\t\aabcdef\xff'.encode('quoted-printable')
Out[56]: '=01\n=08=09=07abcdef=FF'

In [57]: _.decode('quoted-printable')
Out[57]: '\x01\n\x08\t\x07abcdef\xff'
Das kannst Du einfach grundsätzlich machen. ASCII-Buchstaben bleiben dabei auch kodiert noch lesbar und, zumindest in den meisten westlichen Ländern, bestehen Dateinamen zu einem sehr grossen Teil aus ASCII-Zeichen.

Oder Du nimmst 'string-escape' als Kodierung:

Code: Alles auswählen

In [60]: print 'Brief an Manfred Müller.rtf'.encode('string-escape')
Brief an Manfred M\xc3\xbcller.rtf
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

BlackJack hat geschrieben:Eine gute Standardkodierung wäre wohl "Quoted Printable" [...] Oder Du nimmst 'string-escape' als Kodierung
Was ist besser oder geeigneter?

Habe mich im Ürbigen nochmal an den `FlatTree` gewagt und dabei an Leonidas' Code-Snippet orientiert. Hat ne Weile gedauert, bis ich raus hatte, warum in meinem Python 2.4 das import xml.etree.ElementTree nicht geklappt hat... :twisted: Der `ElementTree` scheint doch schon genau das zu können was ich mir so mühselig zusammengekrazt habe... und eigentlich noch mehr, weil er ja gleich für XML gedacht ist. Daher mal meine Idee dazu hier: Paste #540

Es entsteht:

Code: Alles auswählen

<Snapshot>
    <FileSystem>
        <File ID="1" Name="" Type="Drive"/>
        <File ID="2" Name="boot" ParentID="1" Type="Directory"/>
        <File ID="3" Name="grub" ParentID="2" Type="Directory"/>
        <File ID="4" MTime="1167775095" Name="menu.lst" ParentID="3" Size="3893" Type="File"/>
    </FileSystem>
    <WindowsRegistry/>
</Snapshot>
EDIT: Ich stelle gerade fest, dass man als Tag-Namen natürlich hätte "File" bzw. "Directory" nehmen müssen. Dafür kann man dann das Type-Attribut weglassen. Naja es war schon spät 8)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Paar Anmerkungen: Deine Benennung ist nicht PEP8 konform und ich denke ich hätte die Konstanten aus der Klasse rausgezogen. Ich denke dass man CreateID in der Klasse gar nicht braucht und das diese als Generator besser gelöst werden kann. Schließlich erwartet man ja sequenzille IDs. In Zeile 66 brauchst du nicht auf walk == True zu testen, sondern nur auf walk.
Andererseits könnte das durch die Rekursion problematisch werden, denn Python hat ein Rekursionslimit. Rekursionen sind natürlich selbst noch einmal Speicherfressend.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Antworten