Hi,
ist es denkbar, dass man die split() Funktion re-implementiert, dass sie iterable ist, statt immer eine komplette Liste zurückgibt?
Hintergrund der Frage ist die Problematik rund um grosse (oder grössere) Eingabedaten, wo es zu bevorzugen wäre, wenn da keine komplette monster-mörder Liste zurückgegeben wird, stattdessen man die einzelnen split() Resultate "häppchenweise" bekommt.
Quasi eine Funktion, welche die Elemente einer split() Funktion einzeln zurück-yieldet.
Ist split() eigentlich in Python implementiert? Wenn ja, wo kann man den source-code davon angucken?
split() Funktion re-implementieren, dass sie iterable ist.
Klar kann man das machen. In einer Schleife mit `index()`-Methode die Trennzeichenfolge suchen und dann die enstprechenden "slices" ``yield``\en. Man kann `index()` auch einen Startoffset mitgeben. `str.split()` dürfte in C implementiert sein. Quelltext gibt's natürlich auf www.python.org
- gerold
- Python-Forum Veteran
- Beiträge: 5555
- Registriert: Samstag 28. Februar 2004, 22:04
- Wohnort: Oberhofen im Inntal (Tirol)
- Kontaktdaten:
...diese Aussage ist hier eher selten.akis.kapo hat geschrieben:PS: BITTE KEINE LÖSUNG POSTEN!!!
.
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
@akis.kapo: Eher nach `stringobject.c` und da dann nach der Funktion `string_split()`. Ist jedenfalls bei der 3.0er Beta dort zu finden.
-
- User
- Beiträge: 773
- Registriert: Mittwoch 5. November 2003, 18:06
- Wohnort: Schweiz
- Kontaktdaten:
Hi
Also ich würde das so machen:
Dass in einer Schleife bis rest = '' ist und jedesmal das token yielden.
Gruss
*edit* ups split gibt ja nur 1 Element zurück wenns fertig ist, also halt vorher schauen ob len(res) == 2 ist und daraus entscheiden ob fertig oder noch nicht
Also ich würde das so machen:
Code: Alles auswählen
token, rest = data.split(trennzeichen, 1)
Gruss
*edit* ups split gibt ja nur 1 Element zurück wenns fertig ist, also halt vorher schauen ob len(res) == 2 ist und daraus entscheiden ob fertig oder noch nicht
Das ist aber mit sehr viel umkopieren von Daten verbunden. Gerade bei grossen Datenmengen und vielen Elementen hat das eine furchtbare Laufzeit.
@BlackJack
Hier meine erste Version:
Bitte mal durchchecken und kritisieren.
(Aber nicht zu stark kritisieren, sonst guck ich nicht mehr ins Forum!)
Dateien:
test1.txt:
test2.txt:
itersplit.py:
EDIT: Ausgabe vergessen, nachgeliefert:
Irgendwie erinnert mich der Stil aber an C statt Python.
Hier meine erste Version:
Bitte mal durchchecken und kritisieren.
(Aber nicht zu stark kritisieren, sonst guck ich nicht mehr ins Forum!)
Dateien:
test1.txt:
Code: Alles auswählen
aaa//bbb//ccc//ddd//eee
Code: Alles auswählen
Dies ist ein wunderschöner Abschnitt,
der durch andere Abschnitte mit einem
Absatz getrennt wird. Einen Absatz
erkennt man durch zwei aufeinander
folgende Newline Zeichen '\n\n'.
Dies ist ein bescheuerter Abschnitt,
der durch andere Abschnitte durch einen
Absatz getrennt wird. Einen Absatz
erkennt man durch zwei aufeinander
folgende Newline Zeichen '\n\n'.
Dies ist ein überflüssiger Abschnitt,
der durch andere Abschnitte mit einem
Absatz getrennt wird. Einen Absatz
erkennt man durch zwei aufeinander
folgende Newline Zeichen '\n\n'.
Dies ist ein bomchickawahwah Abschnitt,
der durch andere Abschnitte mit einem
Absatz getrennt wird. Einen Absatz
erkennt man durch zwei aufeinander
folgende Newline Zeichen '\n\n'.
Code: Alles auswählen
#!/usr/bin/env python
def iterable_split(filename, sep=None, blocksize=None):
eingabe_band = open(filename,'r') # hier lesen wir
arbeits_band = '' # hier suchen wir
if sep is None:
sep = '\n' # default setzen
if blocksize is None:
blocksize = 512 # default setzen
else:
blocksize = max(blocksize,1) # bs muss > 0 sein
current_block = blocksize * '?' # nur spielerei ...
while not len(current_block) < blocksize: # solange wir "ganze" blöcke lesen ...
current_block = eingabe_band.read(blocksize) # lese genau einen block
arbeits_band += current_block # füge ihn ans ende der arbeitsbands
find_index = 0 # neue suche, neues glück
while find_index > -1: # solange wir was finden ...
find_index = arbeits_band.find(sep) # markiere trefferstartpunkt
if find_index > -1: # wenn wir überhaupt treffen ...
treffer = arbeits_band[:find_index] # merke treffer
arbeits_band = arbeits_band[find_index+len(sep):] # lösche treffer+sep vom anfang des arbeitsbands
yield treffer # gib treffer zurück
if len(arbeits_band) > 0: # falls die dateigrösse in bytes nicht = vielfaches der blockgrösse, gebe "rattenschwanz" zurück
yield arbeits_band # restgeld zurückgeben
for i in iterable_split('test1.txt', '//', 8):
print i
for i in iterable_split('test2.txt', '\n\n'):
print i
Code: Alles auswählen
$ ./itersplit.py
aaa
bbb
ccc
ddd
eee
Dies ist ein wunderschöner Abschnitt,
der durch andere Abschnitte mit einem
Absatz getrennt wird. Einen Absatz
erkennt man durch zwei aufeinander
folgende Newline zeichen '\n\n'.
Dies ist ein bescheuerter Abschnitt,
der durch andere Abschnitte mit einem
Absatz getrennt wird. Einen Absatz
erkennt man durch zwei aufeinander
folgende Newline zeichen '\n\n'.
Dies ist ein überflüssiger Abschnitt,
der durch andere Abschnitte mit einem
Absatz getrennt wird. Einen Absatz
erkennt man durch zwei aufeinander
folgende Newline zeichen '\n\n'.
Dies ist ein bomchickawahwah Abschnitt,
der durch andere Abschnitte mit einem
Absatz getrennt wird. Einen Absatz
erkennt man durch zwei aufeinander
folgende Newline zeichen '\n\n'.
Zuletzt geändert von akis.kapo am Mittwoch 20. Februar 2008, 00:03, insgesamt 2-mal geändert.
-
- Python-Forum Veteran
- Beiträge: 16025
- Registriert: Freitag 20. Juni 2003, 16:30
- Kontaktdaten:
Mein erster Gedanke war: igitt, Inline-Kommentare!
Der zweite war, dass das ja etwas ganz anderes macht, als ``split()``. Deines splittet eine Datei die es auch selbst öffnet (ohne Fehlerbehandlung). Also übergibtst du split_iterable() gar kein Iterable sondern einen Dateinamen (der zwar auch iterabel ist, aber der wird nicht gesplittet). Worum gehts jetzt? Einen Split-Generator oder eine Datei via Generator zu splitten?
Der zweite war, dass das ja etwas ganz anderes macht, als ``split()``. Deines splittet eine Datei die es auch selbst öffnet (ohne Fehlerbehandlung). Also übergibtst du split_iterable() gar kein Iterable sondern einen Dateinamen (der zwar auch iterabel ist, aber der wird nicht gesplittet). Worum gehts jetzt? Einen Split-Generator oder eine Datei via Generator zu splitten?
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Es geht darum, split()-Verhalten nachzubauen, aber statt der Rückgabe einer kompletten Liste, die einzelnen Elemente zu yielden.
Ich habe einfach Dateien gewählt in diesem Prototyp, weil ich mir verschiedene Testdateien erstellt habe.
Man hätte auch Strings definieren können statt mit Dateien zu arbeiten.
Es geht ja ums Prinzip. Das mit dem Dateien kann man ja umbauen.
EDIT:
PS: Mein Programm hier hat keine Kommentare. (Ich hab eh alles im Kopf).
Die Kommentare seht nur ihr im Forum in diesem Post.
Ich habe einfach Dateien gewählt in diesem Prototyp, weil ich mir verschiedene Testdateien erstellt habe.
Man hätte auch Strings definieren können statt mit Dateien zu arbeiten.
Es geht ja ums Prinzip. Das mit dem Dateien kann man ja umbauen.
EDIT:
PS: Mein Programm hier hat keine Kommentare. (Ich hab eh alles im Kopf).
Die Kommentare seht nur ihr im Forum in diesem Post.
Die Funktion sähe massiv anders aus, wenn sie sich nicht um die Datei kümmern würde, sondern wirklich nur eine Zeichenkette splitten würde.
Default-Werte sollte man, wenn das möglich ist, gleich in der Argumentliste angeben. Bei `sep` und `blocksize` hätte man das problemlos tun können, weil sowohl Zeichenketten als auch Zahlen "immutable" sind. Da kann es also nicht zu unerwünschten Nebeneffekten kommen. Und eine Blockgrösse kleiner 1 würde ich nicht "heimlich" korrigieren, sondern höchstens dem Aufrufer als Ausnahme um die virtuellen Ohren hauen. Aus dem Zen: "Errors should never pass silently."
Das blockweise "addieren" und dann das Löschen von Treffer und Separator vom `arbeits_band` ist wieder mit sehr viel unnötigem Umkopieren von Zeichenkettendaten verbunden. Du solltest das `iter_split()` (der Name passt besser zu der Funktion) nur auf einer Zeichenkette definieren und wirklich nur die Treffer dort heraus kopieren und ``yield``\en. Wie gesagt, die `find()`-Methode kennt noch mehr Argumente als nur die gesuchte Zeichenkette!
Und wenn man dass dann auf Dateien anwenden will, die richtig gross sind, so bis 2 GiB auf 32-Bit-Systemen, dann kann man anstatt einer Zeichenkette immer noch ein `mmap.mmap()`-Objekt an die Funktion übergeben und braucht sich nicht selbst um so kleinliches blockweises Einlesen kümmern.
Default-Werte sollte man, wenn das möglich ist, gleich in der Argumentliste angeben. Bei `sep` und `blocksize` hätte man das problemlos tun können, weil sowohl Zeichenketten als auch Zahlen "immutable" sind. Da kann es also nicht zu unerwünschten Nebeneffekten kommen. Und eine Blockgrösse kleiner 1 würde ich nicht "heimlich" korrigieren, sondern höchstens dem Aufrufer als Ausnahme um die virtuellen Ohren hauen. Aus dem Zen: "Errors should never pass silently."
Das blockweise "addieren" und dann das Löschen von Treffer und Separator vom `arbeits_band` ist wieder mit sehr viel unnötigem Umkopieren von Zeichenkettendaten verbunden. Du solltest das `iter_split()` (der Name passt besser zu der Funktion) nur auf einer Zeichenkette definieren und wirklich nur die Treffer dort heraus kopieren und ``yield``\en. Wie gesagt, die `find()`-Methode kennt noch mehr Argumente als nur die gesuchte Zeichenkette!
Und wenn man dass dann auf Dateien anwenden will, die richtig gross sind, so bis 2 GiB auf 32-Bit-Systemen, dann kann man anstatt einer Zeichenkette immer noch ein `mmap.mmap()`-Objekt an die Funktion übergeben und braucht sich nicht selbst um so kleinliches blockweises Einlesen kümmern.
@BJ
Ok, dann hier der Gegenvorschlag:
Allerdings ist mir nicht 100%ig klar, wie ich das mache,
dass es per mystring.iter_split(foo, bar) geht, statt als iter_split(string, ...).
Ok, dann hier der Gegenvorschlag:
Code: Alles auswählen
#!/usr/bin/env python
def iter_split(instring, sep='\n'):
len_sep = len(sep)
find_index = 0
pivot = 0
while find_index > -1:
find_index = instring.find(sep, pivot)
if find_index > -1:
result = instring[pivot:find_index]
pivot = find_index + len_sep
yield result
mystring = 'aaa//bbb//ccc//ddd//eee'
for i in iter_split(mystring, '//'):
print i
dass es per mystring.iter_split(foo, bar) geht, statt als iter_split(string, ...).
-
- Python-Forum Veteran
- Beiträge: 16025
- Registriert: Freitag 20. Juni 2003, 16:30
- Kontaktdaten:
Gar nicht - das ist unterbunden worden (vom BDFL), es ist in Python nicht vorgesehen dass man eingebaute Typen erweitern kann. Was du machen kannst, ist einen neuen Typ zu erstellen und von den eingebauten Typen zu erben.akis.kapo hat geschrieben:Allerdings ist mir nicht 100%ig klar, wie ich das mache,
dass es per mystring.iter_split(foo, bar) geht, statt als iter_split(string, ...).
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Hmm, das habe ich mir nämlich fast schon gedacht.Leonidas hat geschrieben:Gar nicht - das ist unterbunden worden (vom BDFL), es ist in Python nicht vorgesehen dass man eingebaute Typen erweitern kann. Was du machen kannst, ist einen neuen Typ zu erstellen und von den eingebauten Typen zu erben.
Aber aus einem ganz anderem Grund:
Das mmap() Zeugs kann man ja nicht innerhalb der iter_strip() Funktion einbinden,
weil die ja auf einem fertigem String arbeitet (ob als Parameter übergeben oder auf Objekt angewandt, das sei mal hingestellt.).
Das bedeutet auch, dass das "neue" Objekt, dass von string erben soll, irgendwie ne classenmethode oder sonst einen Weg hat,
versteckt vom iter_split() irgendwas zu mmappen, oder sonstwas, welches blockweises lesen ermöglicht.
@BJ
Dann würde ich aber die manuelle Blockweise-Lesen Lösung bevorzugen,
weil mir gehts ja um Speicherersparnis bei riesigen Dateien.
Mmap, so wie ich das verstanden habe, speichert ja alles Doppelt.
Sprich, das Delta im Arbeitsspeicher (ist mutable und kann sich vom Dateiinhalt unterscheiden, solange man nicht closed oder flushed)
und dann noch das nicht unterbindbare Filecache vom OS, welches
das gelesene File cached (unverändert, bzw. halt so, wies gelesen wird.).
Oder nicht, oder was meint ihr?
EDIT:
Ist der nicht unterbindbare Filecache vom OS eigentlich wirklich so ununterbindbar?
Ich weiss von diversen Datenbanksystemen, dass sie für ihre SMS das OS Filecaching "deaktivieren"
(durch nen bestimmten open() Parameter, der das caching unterbindet?).
IBMs AIX soll das können. Die Frage ist kanns jedes Unix/Linux? (Oder gar Windows)
Und wenn ja, wo gibts diese Option in python? (ich seh da nix unter open oder file)
Ich verstehe Deine Bedenken bezüglich `mmap` nicht ganz. Du veränderst die Zeichenkette in `iter_split()` ja nicht, also muss `mmap` sich da auch keine Veränderungen im Speicher merken. Da wird nichts doppelt gespeichert, ausser die Kopien, die Du heraus "sliced", aber das hast Du bei jeder anderen Lösung auch. `mmap` dürfte wesentlich effizienter sein, als manuelles blockweises Lesen, das ist schliesslich *der* Mechanismus auf dem in der Regel die ganze virtuelle Speicherverwaltung vom Betriebssystem aufbaut.
Ebenfalls Edit: Wieso willst Du den Block-Cache unterbinden? Sowie dort Platz gebraucht wird, fliegen die Blöcke von dem `mmap` halt raus. Stört doch nicht weiter. Sofern sie dort überhaupt landen ─ wäre eigentlich unsinnig.
Ebenfalls Edit: Wieso willst Du den Block-Cache unterbinden? Sowie dort Platz gebraucht wird, fliegen die Blöcke von dem `mmap` halt raus. Stört doch nicht weiter. Sofern sie dort überhaupt landen ─ wäre eigentlich unsinnig.