Seite 1 von 1

String gleichmäßig teilen

Verfasst: Freitag 13. Dezember 2013, 15:52
von vegan
Ich will mir ein kleines Skript bauen mit dem ich einen string einlesen kann, diesen dann base64 codiere und diesen codierten String möchte ich jetzt in drei, möglichst gleichlange, Strings zerteilen. Eventuell sollen diese drei Teile dann in einem Array gespeichert werden aber das ist erstmal nebensache, wichtig ist das teilen.

Hier mal etwas Pseudocode:

Code: Alles auswählen

import base64

eingabe = input("Bitte String eingeben: ") #EinString
encoded = base64.b64encode(eingabe) #EinStringEncoded

geteilt = stringteilen(encoded, 3) #Teilt den übergebenen String in 3 gleiche Teile

print geteilt

>>> ['EinSt' , 'ringE' , 'ncoded']

Gibt es da ne build-in Lösung und wenn nein wie geht man da am besten ran sowas selber zu machen?

Re: String gleichmäßig teilen

Verfasst: Freitag 13. Dezember 2013, 16:20
von Hyperion

Re: String gleichmäßig teilen

Verfasst: Freitag 13. Dezember 2013, 16:56
von vegan
Das ist ja perfekt.
grouper() sieht aus, als würde es genau das machen was ich brauche aber es gibt mir immer eine Fehlermeldung aus.
AttributeError: 'module' object has no attribute 'grouper'

Das ist das Skript:
import base64 as b
import itertools


eingabe = input("Bitte gib einen String ein: ")
encode = b.b64encode(eingabe)

geteilt = itertools.grouper(encode, 3)

print geteilt

Re: String gleichmäßig teilen

Verfasst: Freitag 13. Dezember 2013, 16:58
von /me
vegan hat geschrieben:AttributeError: 'module' object has no attribute 'grouper'
grouper ist eine Funktion die itertools verwendet und nicht eine Funktion die in itertools definiert wird. Kopiere dir die Funktion einfach in deinen eigenen Code.

Re: String gleichmäßig teilen

Verfasst: Freitag 13. Dezember 2013, 17:36
von vegan
Ah, sowas hab ich mir fast gedacht aber jetzt bekomme ich noch mehr Fehlermeldungen.

Skript:

Code: Alles auswählen

import base64 as b
import itertools

def grouper(iterable, n, fillvalue=None):
    #"Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)


eingabe = input("Bitte gib einen String ein: ")
encode = b.b64encode(eingabe)


geteilt = grouper(encode, 3)

print eingabe
print encode
print geteilt
Fehlermeldung:
Traceback (most recent call last):
File "C:\Users\Korny\Desktop\test.py", line 15, in <module>
geteilt = grouper(encode, 3)
File "C:\Users\Korny\Desktop\test.py", line 8, in grouper
return zip_longest(*args, fillvalue=fillvalue)
NameError: global name 'zip_longest' is not defined

Re: String gleichmäßig teilen

Verfasst: Freitag 13. Dezember 2013, 17:38
von snafu
Ganz trivial ist das leider nicht zu lösen. Frei nach dem ``grouper()``-Beispiel könnte man sowas machen:

Code: Alles auswählen

from __future__ import division
from itertools import izip_longest, repeat
from math import ceil

def get_parts(s, num_parts):
    part_size = int(ceil(len(s) / num_parts))
    parts = izip_longest(*repeat(iter(s), part_size), fillvalue='')
    return [''.join(part) for part in parts]
Ausgabe:

Code: Alles auswählen

>>> s
'EinStringEncoded'
>>> get_parts(s, 3)
['EinStr', 'ingEnc', 'oded']
Dies entspricht nicht 1:1 deiner Vorgabe. Möchtest du es denn exakt wie die Vorgabe haben? Dann müsste der letzte Teil nämlich bei einer ungeraden Aufteilung mehr Buchstaben haben als seine Vorgänger und die Implementierung wird etwas komplexer.

Re: String gleichmäßig teilen

Verfasst: Freitag 13. Dezember 2013, 17:53
von snafu
Und hier nochmal gemäß Vorgabe und ohne jegliche Imports:

Code: Alles auswählen

def iter_parts(s, num_parts):
    n = len(s) // num_parts
    for i in range(0, (len(s) - n - 1), n):
        yield s[i:(i + n)]
    yield s[(i + n):]
Ausgabe:

Code: Alles auswählen

>>> list(iter_parts(s, 3))
['EinSt', 'ringE', 'ncoded']

Re: String gleichmäßig teilen

Verfasst: Freitag 13. Dezember 2013, 18:09
von vegan
Ok, zuerst muss ich dir danken für die Mühe das für mich schon auszuarbeiten aber ich hätte, nebenbei bemerkt, auch mit der ersten Version leben können.

Davon abgesehn, muss ich Tränen überströmt feststellen dass ich sehr viel Python nachholen muss.

Code: Alles auswählen

    def iter_parts(s, num_parts): #grüner bereich, verstanden
        n = len(s) // num_parts #grüner bereich, verstanden
        for i in range(0, (len(s) - n - 1), n): #gelber bereich, ungefähr verstanden
            yield s[i:(i + n)] #roter bereich, bahnhof
        yield s[(i + n):] #roter bereich, bahnhof
Das ist bei mir aber schon immer so gewesen und wird auch irgendwie nicht besser.
An dem Tag, wo ich ne Documentation von irgendeiner Programiersprache lese und etwas verstehe, lad ich den ganzen Stadteil zum Essen ein. :(

Re: String gleichmäßig teilen

Verfasst: Freitag 13. Dezember 2013, 18:41
von snafu
Hier nochmal ein bißchen robuster:

Code: Alles auswählen

def iter_parts(seq, num_parts):
    if num_parts < 0:
        raise ValueError('num_parts must be >= 0')
    if num_parts > len(seq):
        raise ValueError('num_parts must not exceed sequence length')
    total_size = len(seq)
    n = total_size // num_parts
    stop = total_size - n
    if total_size > num_parts:
        stop -= 1
    for i in range(0, stop, n):
        yield seq[i:(i + n)]
    yield seq[(i + n):]
Damit werden jetzt auch Fälle erwartungsgemäß behandelt, wo die Anzahl der Teilstücke der Anzahl der Buchstaben entspricht, oder anders gesagt: Wenn du in deinem Fall genau 16 Teilstücke angefordert hättest.

EDIT: Da sind noch ein paar Randfälle drin, die fehlerhaft ablaufen. Ich gucke mir das später nochmal an. Muss jetzt weg.

Re: String gleichmäßig teilen

Verfasst: Samstag 14. Dezember 2013, 02:59
von snafu
Hier nochmal die versprochene korrigierte Version (auch wenn es den TE vermutlich nicht mehr wirklich interessiert):

Code: Alles auswählen

def iter_parts(seq, num_parts):
    if num_parts == 0:
        return
    if num_parts < 0:
        raise ValueError('num_parts must be >= 0')
    total_size = len(seq)
    if num_parts > total_size:
        raise ValueError('num_parts must not exceed sequence length')
    part_size, remaining = divmod(total_size, num_parts)
    for i in range(num_parts - 1):
        yield seq[part_size * i : part_size * (i + 1)]
    yield seq[-(part_size + remaining):]
Die Funktion garantiert, dass alle Teilstücke bis n-1 gleich lang sind. Das letzte Teilstück setzt sich aus dem, was dann noch übrig ist, zusammen (was auf dem ersten Blick manchmal komisch aussehen kann, gemäß Definition aber korrekt ist).

Re: String gleichmäßig teilen

Verfasst: Montag 16. Dezember 2013, 14:27
von vegan
Wenn mit TE ich gemeint bin dann, doch, ich bin noch sehr interessiert.
Ich muss auch nochmal sagen, ich kann dir eigentlich garnich genug für deine Mühe danken, das Skript ist echt gut.

Ich würde aber gerne noch ne blöde Frage stellen.
Wie genau heißt dieser Syntax bei

Code: Alles auswählen

yield seq[part_size * i : part_size * (i + 1)]
ist das eine Expression?
Die Frage ist bestimmt voll noobig aber das ist der Teil des Skriptes wo mein Verständniss aufhört, deswegen wollte ich mal wissen wonach ich genau googlen muss.

Davon abgesehen, vielen Dank.

Re: String gleichmäßig teilen

Verfasst: Montag 16. Dezember 2013, 14:43
von BlackJack
@vegan: Hinter dem ``yield``-Schlüsselwort steht ein Ausdruck (engl. „expression”). Das ist allerdings ein sehr allgemeiner Begriff der eigentlich alles beschreibt was man zu einem Wert auswerten kann.

``yield`` macht aus einer Funktion eine Generatorfunktion. Das ist eigentlich das einzige an der Zeile was nicht unbeding in einem Anfängertutorial zu finden ist.

Das PEP zur ``yield``-Anweisung: http://www.python.org/dev/peps/pep-0255/

Re: String gleichmäßig teilen

Verfasst: Montag 16. Dezember 2013, 15:19
von snafu
vegan hat geschrieben:Wenn mit TE ich gemeint bin dann, doch, ich bin noch sehr interessiert.
TE = Thread-Ersteller. Also ja: Vermutlich bist du gemeint. ;)
vegan hat geschrieben:Ich muss auch nochmal sagen, ich kann dir eigentlich garnich genug für deine Mühe danken, das Skript ist echt gut.
Meistens steckt man viel Mühe in Ausarbeitungen, wenn man selber Spaß daran hat. Da ist also ein gewisser Anteil an Eigennutz mit dabei. Aber trotzdem gern geschehen. :)
vegan hat geschrieben:Wie genau heißt dieser Syntax bei

Code: Alles auswählen

yield seq[part_size * i : part_size * (i + 1)]
ist das eine Expression?
Was ``yield`` macht, hat BlackJack ja schon erklärt bzw verlinkt. Dahinter folgt Slicing mit berechneten Werten für den jeweiligen Start- und Stop-Index. Ob das jetzt einfach nur ein ``i`` oder das Ergebnis einer Multiplikation ist, spielt keine Rolle, denn es kommen letztlich ja trotzdem Werte heraus, die für die angeforderte Slicing-Operation verwendbar sind. Und der Rest ist dann nur noch ein bißchen Mathematik. Mach dir ggf klar, welche Start- und Endwerte jeweils rauskämen, wenn man z.B. 3 Schleifendurchläufe hätte. Dann solltest du die Idee dahinter erkennen können.