QString im Unicode?

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.
BlackJack

@snafu: Fehlerquelle über die man sehr leicht stolpern kann sind Dateinamen. Ist das betriebssystemnah? Ich denke auch es ist einfach sauberer wenn man beim/vorm Portieren nach Python 3 jede Zeichenkette explizit als b'' oder u'' markiert, damit kann man dann sicher sein dass man sich alles mal angeschaut hat und nicht einfach nur hofft das die Umstellung schon funktionieren wird.

Zu den Bibliotheken die nicht auf Unicode eingestellt sind gehören auch Teile der Standardbibliothek. `pack()` und `unpack()` aus dem `struct`-Modul mögen als erstes Argument keine Unicodezeichenkette. Was ich auf den ersten Blick etwas überraschend fand, aber dann dachte ich es gibt sicher noch mehr Module die in C geschrieben sind und kein Unicode erwarten bei Argumenten die für den Programmierer auf den ersten Blick aber nach Zeichenkette und nicht nach Bytes aussehen.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Hallo Leute, ich habe jetzt konseqenterweise alle QStrings in Unicode() umgewandelt, jedoch ergibt sich weiterhin das Problem:

Code: Alles auswählen

# function from frm_log_in.py
    def log_in(self):
        dbms='pymysql'
        server_host = unicode(self.ui_login.lineEdit_sevrer_host.text())
        username = unicode(self.ui_login.lineEdit_username.text())
        password = unicode(self.ui_login.lineEdit_password.text())
        database_name = unicode(self.ui_login.lineEdit_database_name.text())
        port = unicode(self.ui_login.lineEdit_port.text())

        dbms_driver = 'pymysql'
        dbm_system = 'mysql'
            
        mysql_connection_result = manage_mysql.connect_to_mysql(dbm_system,
                                                                    dbms_driver,
                                                                    username,
                                                                    password,
                                                                    server_host,
                                                                    port,                                                                   
                                                                    database_name)

# function from a core-module
def connect_to_mysql(dbm_system, dbm_driver, db_user, db_passwd, db_host, db_port, db_name):

    try:
        engine = create_engine('{db_sys}+{db_driver}://{user}:{password}@{host}:{port}/{db}'.format(
                                                                     db_sys=dbm_system,
                                                                     db_driver=dbm_driver,
                                                                     user=db_user,
                                                                     password=db_passwd,
                                                                     host=db_host,
                                                                     port=db_port,
                                                                     db=db_name,
                                                                     connect_args = {'time_zone': '+00:00'},
                                                                     encoding='utf8',
                                                                     echo=True))
self.log_in()
File "D:\Dan\Python\xarphus\xarphus\frm_db_login.py", line 203, in log_in
database_name)
File "D:\Dan\Python\xarphus\xarphus\core\manage_mysql.py", line 152, in connect_to_mysql
echo=True))
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
Er zeigt mir zu letzt auf echo=True.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: Du hast ja auch nichts von dem, was ich geschrieben habe, umgesetzt.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3: Du wolltest, dass die Strings im Format, also in der connect_to_mysql()-Funktion in unicode() umgewandelt werden. Nicht dass ich deine Anmerkung überlese. Ich dachte nur, wenn ich die Strings schon weit vorher in unicode() umgewandelt habe, dann brauche ich die hinterher nicht nochmal in unicode() umzuwandeln. Vielleicht sollte ich nicht erst versuchen selbstständig zu denken.
Benutzeravatar
snafu
User
Beiträge: 6741
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Die Aussage war:
Sirius3 hat geschrieben:@Sophus: Du versuchst da Unicode-Zeichen in einen Byte-String hineinzuformatieren. Das will Python nicht. Du mußt den Format-String auch als Unicode-String angeben.
Und der Format-String ist nach wie vor ein Bytestring. Setze entweder ein "u" vor den String oder verwende den von mir empfohlenen Import. Wie schon angesprochen wurde, kann es sein, dass du bei Nutzung der Import-Variante noch andere Veränderungen im Quelltext vornehmen musst.

Dies ist der Format-String, falls das noch nicht klargeworden ist:

Code: Alles auswählen

'{db_sys}+{db_driver}://{user}:{password}@{host}:{port}/{db}'
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@snafu: Damit ich das richtig verstehe mit dem Format-String:

Code: Alles auswählen

        engine = create_engine('{db_sys}+{db_driver}://{user}:{password}@{host}:{port}/{db}'.format(
                                                                     db_sys=dbm_system,
                                                                     db_driver=dbm_driver,
                                                                     user=db_user,
                                                                     password=db_passwd,
                                                                     host=db_host,
                                                                     port=db_port,
                                                                     db=db_name,
                                                                     connect_args = {'time_zone': '+00:00'},
                                                                     encoding='utf8',
                                                                     echo=True))
Dieser Teil ist damit gemeint, richtig? Oder besser gesagt:

Code: Alles auswählen

engine = create_engine('{db_sys}+{db_driver}://{user}:{password}@{host}:{port}/{db}'.format(...
Denn alles hinterher ist ja schon im Unicode. Daher bin ich ein wenig verwirrt.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Genau, der String vor .format ist gemeint.
Und damit der zweite Satz nicht in Vergessenheit gerät zitiere ich mich selbst:
Sirius3 hat geschrieben:Außerdem handelt es sich um eine URL, das heißt, Du mußt Sonderzeichen (die ja v.a. in Passwörtern vorkommen können) kodieren.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3: Format-String ist erst einmal erledigt:

Code: Alles auswählen

        engine = create_engine(unicode('{db_sys}+{db_driver}://{user}:{password}@{host}:{port}/{db}').format(
                                                                     db_sys=dbm_system,
                                                                     db_driver=dbm_driver,
                                                                     user=db_user,
                                                                     password=db_passwd,
                                                                     host=db_host,
                                                                     port=db_port,
                                                                     db=db_name,
                                                                     connect_args = unicode({'time_zone': '+00:00'}),
                                                                     encoding=u'utf8',
                                                                     echo=(True)))
Ist es auch möglich, das Benutzernamen, die im Datenbank-System angelegt werden, auch Umlaute haben können? Und wie sieht es mit dem Namen der Datenbank und der Tabellen aus? Können die auch Umlaute erhalten oder wird das vom Datenbank-System "unterbunden"? Und das mit dem Passwort. Kodieren?

Um meine Gedanken nachvollziehbar zu machen:
1. Ich erhalte das Passwort mit dem Umlaut von einem QString (QlineEdit)
2. Ich habe nun das String-Format in unicode() umgewandelt
2. Nun muss ich die Sonderzeichen kodieren. Wo?
Benutzeravatar
snafu
User
Beiträge: 6741
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Qt Strings sind standardmäßig in Latin-1 kodiert. Damit funktionieren auch deutsche Umlaute, französische Accents und weitere westeuropäische Zeichen. Du wirst nicht alle Zeichen der Erde verarbeiten können, aber eben die aus "unseren Breitengraden".

Probier es doch einfach mal aus: Erstelle einen Qt-String (``from PySide.QtCore import QString``) mit Umlauten ohne Angabe des Encoding und rufe auf diesen String ein ``print unicode(dein_string)`` auf. Dann siehst du ja, was passiert. ;)
BlackJack

@snafu: `QString` sind Unicode, haben also keine Kodierung, jedenfalls keine die den Programmierer interessieren müsste, weil das ein Implementierungsdetail ist. Wenn man einen `QString` ohne Kodierungsangabe umwandelt, egal in welche Richtung, *dann* nimmt Qt offenbar Latin-1 an. Der Punkt ist: Man kann mit `QString` natürlich mehr als den Latin-1-Zeichenvorrat verarbeiten.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@snafu: Du sagtest, dass man nicht alle Zeichen der Welt verarbeiten können, richtig? Wie handhaben größere Software-Produkte das? Also Produkte, die auch in China oder in Russland Einsatz finden? Werden diese Produkte für solche "exotischen" Zeichen neu kompiliert?

Aber zu meinem Problem. Meinst du etwa, dass ich diese Zeile:

Code: Alles auswählen

# -*- coding: utf-8 -*-


am Anfang eines jeden Moduls setzen soll? Bitte, sage mir, dass es falsch ist :) Denn nach meinem Verständnis ist dies nur dann wichtig, wenn man Strings mit Umlaute im Modul hinterlegt, richtig? Ich schäme mich jetzt schon für diese Frage :oops:
Benutzeravatar
snafu
User
Beiträge: 6741
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Stimmt. Der Unicode-String selbst hat natürlich keine Kodierung.
Benutzeravatar
snafu
User
Beiträge: 6741
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Sophus hat geschrieben:@snafu: Du sagtest, dass man nicht alle Zeichen der Welt verarbeiten können, richtig? Wie handhaben größere Software-Produkte das? Also Produkte, die auch in China oder in Russland Einsatz finden? Werden diese Produkte für solche "exotischen" Zeichen neu kompiliert?
Nein. Die können genau so ihre Textangaben in Unicode speichern und für Anzeige beim Benutzer eben eine ländergeeignete Kodierung verwenden. `QString`s besitzen zum Beispiel eine Methode, um UTF-8 zu verwenden. Allein damit deckt man schon deutlich mehr ab.
Sophus hat geschrieben:Aber zu meinem Problem. Meinst du etwa, dass ich diese Zeile:

Code: Alles auswählen

# -*- coding: utf-8 -*-


am Anfang eines jeden Moduls setzen soll? Bitte, sage mir, dass es falsch ist :) Denn nach meinem Verständnis ist dies nur dann wichtig, wenn man Strings mit Umlaute im Modul hinterlegt, richtig? Ich schäme mich jetzt schon für diese Frage :oops:
Nein, das meinte ich nicht. Ich meinte ``from __future__ import unicode_literals``. Aber für dich als Anfänger ist vielleicht das "u" geeigneter.

Das mit dem letzten Teil ist korrekt. Die Angabe bezieht sich in der Tat auf Zeichen außerhalb von ASCII im Quelltext.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@snafu: Entschuldige, wenn ich dich noch einmal belästigen muss. Siriu3 hat mich ja auf das Passwort hingewiesen, welches durchaus Umlaute haben könnte. Womöglich ist es im gegenwärtigen Datenbanksystemen erlaubt, dass auch Datenbanken und auch Tabellen im Namen Umlaute enthalten dürfen. Darüber weiß ich leider noch nichts. Aber wohin soll das "u"?

Denn im View()-Modul wurde das Passwort ja schon in Unicode() umgewandelt:

Code: Alles auswählen

    def log_in(self):
        server_host = unicode(self.ui_login.lineEdit_sevrer_host.text())
        username = unicode(self.ui_login.lineEdit_username.text())
        password = unicode(self.ui_login.lineEdit_password.text())
        database_name = unicode(self.ui_login.lineEdit_database_name.text())
        port = unicode(self.ui_login.lineEdit_port.text())
            
        mysql_connection_result = manage_mysql.connect_to_mysql(dbm_system,
                                                                    dbms_driver,
                                                                    username,
                                                                    password,
                                                                    server_host,
                                                                    port,    
Und der Format-String ist ebenfalls in unicode() umgewandelt worden:

Code: Alles auswählen

        engine = create_engine(unicode('{db_sys}+{db_driver}://{user}:{password}@{host}:{port}/{db}').format(
                                                                     db_sys=dbm_system,
                                                                     db_driver=dbm_driver,
                                                                     user=db_user,
                                                                     password=db_passwd,
                                                                     host=db_host,
                                                                     port=db_port,
                                                                     db=db_name,
                                                                     connect_args = {'time_zone': '+00:00'},
                                                                     encoding='utf8',
                                                                     echo=(True)))
Benutzeravatar
snafu
User
Beiträge: 6741
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Das `unicode()` für den Format-String ersetzt das "u". Eigentlich ist es üblich, vor String-Literalen ein "u" zu setzen, wenn man Unicode haben möchte. Dann wird direkt ein Unicode-String erzeugt. Bei dir ist es jetzt so, dass ein Bytestring erzeugt wird und dieser wird anschließend in Unicode umgewandelt. Das ist halt ein unnötiger Zwischenschritt und mehr Schreibarbeit. Technische Nachteile, die das mit sich bringen könnte, fallen mir spontan nicht ein.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@snafu: In Bezug auf das Format-String. ich dachte mir einfach, dass ich alle Format-String in einem Schlag in unicode() umwandle, und nicht in jedem einzelnen Format-String ein "u" davor setze. Da hätte ich am Ende mehr "u"s als ich möchte. Daher habe ich es so erledigt. Aber wo genau wird nun erst ein Bytestring und dann erst ein Unicode erzeigt? Meinst du den Punkt in der log_in()-Funktion, wo ich jedes einzelne QString in unicode() umwandle?
Benutzeravatar
snafu
User
Beiträge: 6741
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Alle Format-Strings? Du hast einen einzigen Format-String! Das andere sind die Argumente für den Format-String - also dass, was eingesetzt werden soll. Und man kann das "u" nur verwenden, wenn man selbst einen String mittels Literalen erzeugt. Für deine anderen Strings würde das "u" nicht funktionieren, da sie ja erst zur Laufzeit bekannt sind.

Den Bytestring hast du immer dann, wenn du unter Python 2 einen String mittels String-Literalen erzeugst. Somit erzeugt ``unicode('spam')`` den Bytestring "spam". Wenn man diesen Bytestring an die `unicode()`-Funktion übergibt (was in dem Fall ja auch passiert), dann liefert sie einen Unicode-String zurück. Im Gegensatz dazu würde ein ``u'spam'`` direkt den Unicode-String erzeugen.

Und wie kommst du jetzt darauf, dass ein `unicode()`-Aufruf auf einen `QString` einen Bytestring als Zwischenschritt erzeugt? Es wurde doch schon erklärt, dass gerade dies *nicht* passiert.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@snafu: Dann habe ich dich falsch gelesen. Ich nahm an, dass du bei mir einen sogenannten "Zwischenschritt" entdeckt hast. Aber zu Sirius3s Anmerkung, das mit dem Passwort. Hat es sich hiermit erledigt? Er schrieb ja was von Kodierung. Ich zitiere ihn mal:
Sirius3 hat geschrieben:Außerdem handelt es sich um eine URL, das heißt, Du mußt Sonderzeichen (die ja v.a. in Passwörtern vorkommen können) kodieren.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Ich war gerade mal so frei, und habe chinesische Zeichen in meine Anmelde-Maske eingegeben. Alle Variablen und Strings sind ja nun in unicode() umgewandelt werden. Raus kam dabei folgende Fehlermeldung:
Traceback (most recent call last):
File "D:\Dan\Python\xarphus\xarphus\frm_db_login.py", line 170, in disable_connect_bitton
self.log_in()
File "D:\Dan\Python\xarphus\xarphus\frm_db_login.py", line 202, in log_in
database_name)
File "D:\Dan\Python\xarphus\xarphus\core\manage_mysql.py", line 155, in connect_to_mysql
Base.metadata.create_all(engine)
File "C:\Python27\lib\site-packages\sqlalchemy\sql\schema.py", line 3634, in create_all
tables=tables)
File "C:\Python27\lib\site-packages\sqlalchemy\engine\base.py", line 1851, in _run_visitor
with self._optional_conn_ctx_manager(connection) as conn:
File "C:\Python27\lib\contextlib.py", line 17, in __enter__
return self.gen.next()
File "C:\Python27\lib\site-packages\sqlalchemy\engine\base.py", line 1844, in _optional_conn_ctx_manager
with self.contextual_connect() as conn:
File "C:\Python27\lib\site-packages\sqlalchemy\engine\base.py", line 2035, in contextual_connect
self._wrap_pool_connect(self.pool.connect, None),
File "C:\Python27\lib\site-packages\sqlalchemy\engine\base.py", line 2070, in _wrap_pool_connect
return fn()
File "C:\Python27\lib\site-packages\sqlalchemy\pool.py", line 376, in connect
return _ConnectionFairy._checkout(self)
File "C:\Python27\lib\site-packages\sqlalchemy\pool.py", line 708, in _checkout
fairy = _ConnectionRecord.checkout(pool)
File "C:\Python27\lib\site-packages\sqlalchemy\pool.py", line 480, in checkout
rec = pool._do_get()
File "C:\Python27\lib\site-packages\sqlalchemy\pool.py", line 1055, in _do_get
self._dec_overflow()
File "C:\Python27\lib\site-packages\sqlalchemy\util\langhelpers.py", line 60, in __exit__
compat.reraise(exc_type, exc_value, exc_tb)
File "C:\Python27\lib\site-packages\sqlalchemy\pool.py", line 1052, in _do_get
return self._create_connection()
File "C:\Python27\lib\site-packages\sqlalchemy\pool.py", line 323, in _create_connection
return _ConnectionRecord(self)
File "C:\Python27\lib\site-packages\sqlalchemy\pool.py", line 449, in __init__
self.connection = self.__connect()
File "C:\Python27\lib\site-packages\sqlalchemy\pool.py", line 602, in __connect
connection = self.__pool._invoke_creator(self)
File "C:\Python27\lib\site-packages\sqlalchemy\engine\strategies.py", line 97, in connect
return dialect.connect(*cargs, **cparams)
File "C:\Python27\lib\site-packages\sqlalchemy\engine\default.py", line 385, in connect
return self.dbapi.connect(*cargs, **cparams)
File "C:\Python27\lib\site-packages\pymysql\__init__.py", line 88, in Connect
return Connection(*args, **kwargs)
File "C:\Python27\lib\site-packages\pymysql\connections.py", line 644, in __init__
self._connect()
File "C:\Python27\lib\site-packages\pymysql\connections.py", line 838, in _connect
self._request_authentication()
File "C:\Python27\lib\site-packages\pymysql\connections.py", line 1007, in _request_authentication
_scramble(self.password.encode('latin1'), self.salt)
UnicodeEncodeError: 'latin-1' codec can't encode characters in position 0-2: ordinal not in range(256)
Eine Verdammt lange Fehlermeldung :shock:
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: die große Frage ist, ob mysql überhaupt etwas von Encoding weiß. CREATE USER erlaubt die Angabe von Unicode-Username und -Password, wie das die Connection-URL umsetzt müßte man testen.
Ein korrekte URL sähe sowieso so aus:

Code: Alles auswählen

import urllib
def quote_unicode(value):
    return urllib.quote(unicode(value).encode('utf-8'), safe='')

def make_url(self):
        server_host = quote_unicode(self.ui_login.lineEdit_sevrer_host.text())
        username = quote_unicode(self.ui_login.lineEdit_username.text())
        password = quote_unicode(self.ui_login.lineEdit_password.text())
        database_name = quote_unicode(self.ui_login.lineEdit_database_name.text())
        port = int(self.ui_login.lineEdit_port.text())
           
        connection_url = b'{db_sys}+{db_driver}://{user}:{password}@{host}:{port}/{db}'.format(
            db_sys=dbm_system, db_driver=dbm_driver, user=username, password=password,
            host=server_host, port=port, db=database_name)
 
Antworten