base64

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
mechanicalStore
User
Beiträge: 111
Registriert: Dienstag 29. Dezember 2009, 00:09

Hallo Zusammen,

Das Folgende funktioniert, sieht aber schrecklich aus. Wie lässt sich das optimieren (nur ein Test, bitte nicht die Variablenbezeichnungen monieren)? Ich will das base64 codierte als normalen String weiter verarbeiten können:

Code: Alles auswählen

>> a = 'Worte ueber Worte'
>>> b = a.encode('ascii')
>>> c = base64.b64encode(b)
>>> d = str(c)
>>> e = d[2:-1]
>>> e
'V29ydGUgdWViZXIgV29ydGU='
Was kann ich im Folgenden machen?:

Code: Alles auswählen

>>> a = base64.b64encode(b'Worte über Worte')
File "<stdin>", line 1
a = base64.b64encode(b'Worte über Worte')
^
SyntaxError: bytes can only contain ASCII literal characters
Kann man Umlaute also garnicht in base64 darstellen?

Danke für Antworten.
Benutzeravatar
sparrow
User
Beiträge: 4187
Registriert: Freitag 17. April 2009, 10:28

Normale Zeichenketten (also keine bytes) haben in Python eine Kodierung. Damit du die vernünftig in base64 wandeln kannst, müssen es reine byte-Folgen sein. Dafür besitzen Zeichenketten die .decode() Methode.
Und ja, es ist wichtig sich die ursprüngliche Codierung zu merken, denn die Byte-Folge möchtest du ggf. wieder zurück wandeln, denn: Das Ergebnis der b64-Encodings ist kein "normaler String" sondern wieder eine byte Folge. Die in seine intern verwendete Zeichenkettendarstellung zu wandeln ist idR falsch.

Wenn man schon weiß, dass man schlechte Variablennamen verwendet würde ich es als zielführender erachten, gute zu verwenden, statt sich zu verbieten, das zu bemängeln.
Also: Deine Namen sind auch Für ein Beispiel scheiße. Es wird nämlich nicht ersichtlich, warum du den Unsinn mit der Zuweisung an e machst. Das würden vernünftige Namen vielleicht ändern. Oder du würdest merken, dass das Unsinn ist.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Zeichenketten haben in Python KEINE Kodierung, es sind einfach Zeichen. Um sie in Bytes darstellen zu können, müssen sie encodiert werden, dafür besitzen Zeichenketten die encode-Methode. Üblich ist es UTF8 als Kodierung zu verwenden, denn damit können alle definierten Zeichen codiert werden. Die ASCII-Kodierung enthält z.B. keine Umlaute, ist also für Deinen Bedarf ungeeignet.
Dann kann man die Bytes in Base64 darstellen, was dann wieder zu Bytes führt, da die aber nur Buchstaben, Zahlen und ein paar Sonderzeichen umfasst, die alle im ASCII-Bereich liegen, kann man sie einfach per ASCII decodieren.
Mit str(c) wird aber eine Stringrepräsentation des Bytes-Objektes erstellt, das rein für Debuggingzwecke gedacht ist, und nicht für die Weiterverarbeitung im Programm. Das Warnsignal sollte hier schon sein, dass per Slicing ein paar Zeichen abgeschnitten werden. Man muß nicht jedes Zwischenergebnis an eine Variable binden und auch für Beispiele sind mehr als ein Buchstabe für eine Variable erlaubt.

Code: Alles auswählen

text = 'Worte über Worte'
encoded = base64.b64encode(text.encode('utf8')).decode('ASCII')
mechanicalStore
User
Beiträge: 111
Registriert: Dienstag 29. Dezember 2009, 00:09

Danke, die Beanstandungen sind angekommen. Jeglicher Erklärungsversuch wäre jetzt sinnleer... Aber folgendes ist noch anzumerken. Das Ganze muss wieder in eine Zeichenkette umgeformt werden, da daraus später ein Link gebaut wird. Deshalb die offenbar 'unnötige' Wandlung von d nach e. Denn:

Code: Alles auswählen

a = 'Worte ueber Worte'
b = a.encode('ascii')
c = base64.b64encode(b)
d = str(c)
e = d[2:-1]
print(d)
print(e)
link = f'blabla://serverpraefix/{e}/suffix'
print(link)
b'V29ydGUgdWViZXIgV29ydGU='
V29ydGUgdWViZXIgV29ydGU=
blabla://serverpraefix/V29ydGUgdWViZXIgV29ydGU=/suffix
Der Inhalt von d ist dafür unbrauchbar, daher das Abschneiden von b' und '
nezzcarth
User
Beiträge: 1633
Registriert: Samstag 16. April 2011, 12:47

mechanicalStore hat geschrieben: Freitag 30. September 2022, 17:25 Der Inhalt von d ist dafür unbrauchbar, daher das Abschneiden von b' und '
Hast du das Beispiel von Sirius3 einmal im interaktiven Interpreter ausprobiert? Das macht genau das, was du gefragt hattest, aber auf dem korrekten Weg – "encoded" ist, wie gewünscht, ein String. Das " b' " nachträglich zu entfernen, ist sehr umständlich, fehleranfällig und auch nicht notwendig.
mechanicalStore
User
Beiträge: 111
Registriert: Dienstag 29. Dezember 2009, 00:09

Hast du das Beispiel von Sirius3 einmal im interaktiven Interpreter ausprobiert? Das macht genau das, was du gefragt hattest, aber auf dem korrekten Weg – "encoded" ist, wie gewünscht, ein String. Das " b' " nachträglich zu entfernen, ist sehr umständlich, fehleranfällig und auch nicht notwendig.
Ja natürlich. Ausprobiert und auch eingesetzt. Genau danach hatte ich ja auch Anfangs gefragt, nach einer "richtigen" Lösung. Ich wollte nur aufklären, wieso ich die Zeichenkette "beschnitten" hatte, weil das in Frage gestellt wurde.

Sirius3's Lösung ist top und auf dem Weg, besten dank Sirius3.
mechanicalStore
User
Beiträge: 111
Registriert: Dienstag 29. Dezember 2009, 00:09

Hallo Zusammen,

ich möchte noch mal auf das Thema zurück kommen:

Code: Alles auswählen

import base64

def main():
    text = "Worte über Worte"
    utf8_encoded_text = text.encode('utf8')
    base64_encoded_text = base64.b64encode(utf8_encoded_text)
    ascii_decoded_text = base64_encoded_text.decode('ASCII')

    directly_utf8_encoded_text = u'Worte über Worte'

    print(f'{text}\n{utf8_encoded_text}\n{base64_encoded_text}\n{ascii_decoded_text}\n')
    print(f'{directly_utf8_encoded_text}')


if __name__ == '__main__':
    main()
Ausgabe:
Worte über Worte
b'Worte \xc3\xbcber Worte'
b'V29ydGUgw7xiZXIgV29ydGU='
V29ydGUgw7xiZXIgV29ydGU=

Worte über Worte
Frage zu Wie ist der ursprünglich im Speicher codiert? Sirius3 schreibt
Zeichenketten haben in Python KEINE Kodierung
sparrow schreibt
Normale Zeichenketten (also keine bytes) haben in Python eine Kodierung
.
Zweite Frage: Warum sind die Ausgaben von

Code: Alles auswählen

directly_utf8_encoded_text
utf8_encoded_text
nicht identisch?

Danke und Gruß
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

utf8_encoded_text ist ein Bytesstring, dessen Inhalt utf8-kodiert ist, directly_utf8_encoded_text ist ein Unicodestring, also gar nicht kodiert weder direkt noch mit utf8. Warum also sollten die beiden identisch sein?

Was Du wahrscheinlich meinst ist:

Code: Alles auswählen

directly_encoded_text = b'Worte über Worte'
Dieser String ist mit dem Encoding, in dem auch die Python-Datei kodiert ist, gespeichert. Wann die Python-Datei utf8-kodiert ist, dann ist dieser Bytestring das auch, wenn nicht, dann nicht.
mechanicalStore
User
Beiträge: 111
Registriert: Dienstag 29. Dezember 2009, 00:09

Hallo Sirius3,

danke für Deine Antwort. Falls ich es richtig verstanden habe, würde das folgendes bedeuten;

Die Standardcodierung meines Editors ist auf utf8 eingestellt, d.h. sämtliche Literale liegen also auch als utf8-transformierte Bytefolge im Speicher. Von daher deute ich Deine Aussage
directly_utf8_encoded_text ist ein Unicodestring, also gar nicht kodiert
so, dass die Bytefolge im Speicher grundsätzlich der von unicode nach utf8 transformierten Bytefolge entsprechen muss, aber eben nicht als string codiert ist. In dem Fall müssten also auch

Code: Alles auswählen

directly_utf8_encoded_text = u'Worte über Worte'
directly_utf8_encoded_text = 'Worte über Worte'
das Gleiche bedeuten, da das Literal "sowieso" als utf8 transformierte Bytefolge gespeichert wird?!

Wenn ich aber in meinem Editor (Visual Studio Code) das von Dir u.g. Beispiel

Code: Alles auswählen

directly_encoded_text = b'Worte über Worte'
eingebe, sollte das ja die Bytefolge als String wiedergeben, wird aber sofort moniert mit der Meldung
Non-ASCII character not allowed in bytes string literal
was ja eigentlich im Widerspruch zu der Einstellung utf8 des Editors steht?!
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Ah, ok, dann meckert da Python schon, was ja auch sinnvoll ist, denn die Kodierung der Python-Datei kann sich ja auch einfach ändern.
Dann ist das hier die richtige Form:

Code: Alles auswählen

directly_utf8_encoded_text  = b'Worte \xc3\xbcber Worte'
In welcher Art und Weise Unicode-Strings im Speicher vorliegen, ist nicht spezifiziert, das kann also jede Python-Implementierung so machen, wie es am praktischsten ist, also in der CPython-Implementierung als UTF32. Die Transformation von literalen Zeichenketten findet dabei bereits beim Einlesen der py-Datei statt.
Benutzeravatar
__blackjack__
User
Beiträge: 13080
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@mechanicalStore: Bei dem hier…

Code: Alles auswählen

directly_utf8_encoded_text = u'Worte über Worte'
directly_utf8_encoded_text = 'Worte über Worte'
…bedeutet das tatsächlich beides das gleiche, weil das u als Präfix in Python 3 keine Funktion hat. Das ist syntaktisch erlaubt, um es einfacher zu machen alten Python 2 Quelltext zu portieren, wo dieser Präfix tatsächlich einen Unterschied macht. In Python 2 ist der b-Präfix dafür ohne Wirkung/Funktion, der in Python 3 aber einen Unterschied macht.

Der Name in dem Beispiel ist Falsch, weil das `encoded` darin nicht stimmt und auch das UTF-8 zwar stimmen kann, aber nicht muss.

Wenn Dein Editor auf UTF-8 eingestellt ist, dann ist der Quelltext als UTF-8 kodierte Bytefolge in der Datei. Wie die Zeichenkette dann im Speicher vorliegt ist nicht definiert. Das ist ein Implementierungsdetail. Und in aktuellen CPython-Versionen gibt da sogar nicht nur eine Kodierung die immer für alle Zeichenketten verwendet wird. Aber das sind alles Interna, die den Programmierer nicht wirklich etwas angehen.

Das nicht-ASCII-Zeichen nicht in Bytekettenliteralen erlaubt sind, ist eine Entscheidung der Python-Entwickler. Und die würde ja im “Widerspruch“ zu *jeder* Kodierungseinstellung im Editor stehen, ausser ASCII — sofern man das irgendwie als Widerspruch auffassen/sehen will. Ich sehe das als Feature, das man expliziter sein muss was da genau drin sein soll und das auch nicht so leicht falsch machen kann, wenn man mal etwas aus einer Datei in eine andere kopiert wo ja auch keine Kodierungsinformationen übertragen werden, sondern nur Text.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Das Encoding gibt nur an, wie Daten in eine Datei (oder einen Stream) geschrieben werden, oder wie sie beim Lesen zu interpretieren sind – nicht aber, wie die Abbildung innerhalb eines Programms erfolgt.
D. h. der Editor zeigt dir den Code an, Python hingegen führt ihn aus, und da bekommt eine mit b' eingeleitete Zeichenfolge (unabhängig von der Codierung der gelesenen Datei) eine Bedeutung, die nachfolgende Zeichen nur im unteren ASCII Bereich erlaubt. Daher die Fehlermeldung.
mechanicalStore
User
Beiträge: 111
Registriert: Dienstag 29. Dezember 2009, 00:09

Hallo Zusammen,

danke Euch für die Erklärungen. Ich war davon ausgegangen, dass die Bytefolgen beim Einlesen 1:1 im gespeicherten Format in den Speicher übertragen werden. Da stellt sich dann noch die Frage, woher Python weiß, wie die Daten vorliegen. Da es sich ja um einfache Textdateien handelt, existieren in der Datei keine Metadaten, die anzeigen, wie das Format ist (oder doch??).

Dass u'xxx' nur aus Kompatibilitätsgründen noch existiert, wusste ich nicht, meine Argumentation, dass das gleich sein muss, war ja eine Andere, aber ok, dass das aufgeklärt ist. :o

Dann stellt sich nur noch die eine Frage: Was genau passiert bei

Code: Alles auswählen

text  = b'Worte \xc3\xbcber Worte'
? Ich erzeuge ein Bytefolge (bzw. Bytestring) im Speicher, die dem von mir (manuell erzeugten) gewählten Encoding entspricht, welches im Speicher aber so abliegt, wie Python das (je nach Implementaion) intern für richtig hält?
Benutzeravatar
sparrow
User
Beiträge: 4187
Registriert: Freitag 17. April 2009, 10:28

Das ist einfach eine Folge von Bytes. Es ist unwahrscheinlich, dass die als etwas anderes als eben das abgelegt wird.
Es ging ja um Unicode-Strings, bei denen von der Implementation abhängt, wie sie im Speicher abgelegt werden.
mechanicalStore
User
Beiträge: 111
Registriert: Dienstag 29. Dezember 2009, 00:09

sparrow hat geschrieben: Montag 3. Juli 2023, 17:30 Das ist einfach eine Folge von Bytes. Es ist unwahrscheinlich, dass die als etwas anderes als eben das abgelegt wird.
Es ging ja um Unicode-Strings, bei denen von der Implementation abhängt, wie sie im Speicher abgelegt werden.
Ok. Das leuchtet ein. Dann verstehe ich allerdings nicht, wie der Zusammenhang mit Sirius3's Antwort ist:
Ah, ok, dann meckert da Python schon, was ja auch sinnvoll ist, denn die Kodierung der Python-Datei kann sich ja auch einfach ändern.
Dann ist das hier die richtige Form:

Code: Alles auswählen

directly_utf8_encoded_text  = b'Worte \xc3\xbcber Worte'
Dort schreibt er ja von einer Kodierung, statt einer Bytefolge ?! Oder liegt das nur daran, dass das 'ü' nicht im ASCII-Zeichensatz abgebildet wird?
Benutzeravatar
sparrow
User
Beiträge: 4187
Registriert: Freitag 17. April 2009, 10:28

Er schreibt von der Kodierung der Python Datei.
Also der Darei in der der Quelltext steht.
Benutzeravatar
__blackjack__
User
Beiträge: 13080
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@mechanicalStore: Wenn keine Metadaten in der Quelltextdatei vorliegen, geht Python 3 von UTF-8 aus. Will man was anderes nutzen, muss man Metadaten in einer der beiden ersten Zeilen als Kommentar unterbringen. Also so etwas wie "# coding: cp1252". Das genaue Format wie so ein Kodierungskommentar aussehen muss, beziehungsweise wie der aussehen kann um erkannt zu werden, steht irgendwo in der Python-Dokumentation.

Der Interpreter liest die Quelltextdatei, oder zumindest maximal die ersten beiden Zeilen als Binärdaten ein und sucht nach dem Kommentar. Je nachdem ob er fündig wird, wird dann die Datei in der entsprechenden Kodierung gelesen/dekodiert.

Bei ``text = b'Worte \xc3\xbcber Worte'`` wird eine Bytekette mit den Codepoint-Werten der Zeichen im Speicher abgelegt. Die ASCII-Zeichen entsprechen ja den Bytewerten für ASCII-Zeichen, und "\xc3" und "\xbc" haben ja, egal wie die Quelltextdatei kodiert war, immer den Wert 0xC3 und 0xBC. Das betrifft dann auch die Anmerkung das sich die Kodierung der Quelltextdatei ändern kann, ohne dass das Einfluss auf den Wert dieses Byteketten-Literals hat. Vorausgesetzt natürlich man wählt eine Kodierung, die alle Zeichen enthält die im Quelltext und im ASCII-Zeichensatz enthalten sind. Muss man ja aber, denn sonst könnte man den Quelltext an sich ja schon nicht in so einer Kodierung speichern.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
mechanicalStore
User
Beiträge: 111
Registriert: Dienstag 29. Dezember 2009, 00:09

Hallo Zusammen,

Alles klar, habe jetzt (hoffentlich) alles verstanden. Besten Dank nochmals für Eure hilfreichen Antworten.

Grüße
Antworten