Query in csv-schreiben und Umlaute ersetzen

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
crazyfile
User
Beiträge: 2
Registriert: Mittwoch 19. April 2017, 11:00

Query in csv-schreiben und Umlaute ersetzen

Beitragvon crazyfile » Mittwoch 19. April 2017, 11:22

Aus einer Oracle Datenbank frage möchte ich eine Abfrage in eine csv-Datei schreiben. Das funktioniert soweit prima und geht auch sehr fix. Jedoch stehe ich nun vor dem Problem, aus einem Feld die Sonderzeichen und Umlaute zu entfernen oder zu ersetzen. Da ich diese Aufgabe in die Query packen wollte um das Python-Skript schmal zu halten, lautet die Abfrage entsprechend für das betreffende Datenbankfeld:

REGEXP_REPLACE(REPLACE(REPLACE(REPLACE(replace(LOWER(pers.name),'ü','ue'),'ä','ae'),'ö','oe'),'ß','ss'), '[^a-z]', '') AS RName

Zunächst möchte ich nur Kleinbuchstaben verwenden (lower) und Sonderzeichen ausschließen (REGEXP mit [^a-z]). Das alleine funktioniert. Dann habe ich über verkettete REPLACE-Befehle in der Query die Sonderzeichen "ersetzt". Auch das wird mit in der Abfrage direkt in der Datenbank korrekt (wie gewünscht) ausgegeben. Leider jedoch nicht mit dem Python-Skript.

Die Query habe ich in einer Textdatei gespeichert und lese ich ein. Irgendwo hakt es doch, oder? Stehe auf dem Schlauch.
  1. import cx_Oracle
  2. import csv
  3. from datetime import datetime
  4. econnection = cx_Oracle.connect("user/Passwort@Datenbank")
  5.  
  6. #Datensatzcursor
  7. ecursor = econnection.cursor()
  8.  
  9. #Datei mit Query öffnen
  10. f1 = open('C:/Python34/Scripts/user2.sql')
  11. esql = f1.read()
  12. f1.close()
  13.  
  14. #Abfrage ausführen
  15. ecursor.execute(esql)
  16. result = ecursor.fetchall()
  17.  
  18. print("Es wurden " + str(ecursor.rowcount) + " Datensätze gefunden.")
  19.  
  20. #Schreiben in csv-Datei
  21. with open('C:/Python34/' + datetime.strftime(datetime.now(),"%Y%m%d%H%M%S") + '_Userdaten.csv','w') as fp:
  22.     myfile = csv.writer(fp,delimiter=';',lineterminator='\n',quoting=csv.QUOTE_NONNUMERIC)
  23.     myfile.writerow([i[0] for i in ecursor.description])
  24.     myfile.writerows(result)
  25.  
  26. fp.close()
  27.  
  28. econnection.close
Zuletzt geändert von BlackJack am Mittwoch 19. April 2017, 11:30, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Benutzeravatar
BlackJack
Moderator
Beiträge: 31901
Registriert: Dienstag 25. Januar 2005, 23:29
Wohnort: Berlin
Kontaktdaten:

Re: Query in csv-schreiben und Umlaute ersetzen

Beitragvon BlackJack » Mittwoch 19. April 2017, 11:49

@crazyfile: Ich würde darauf tippen das die Kodierung der Datei nicht mit der Systemkodierung übereinstimmt.

Wenn man Textdateien als Text einliest, sollte man immer explizit die Kodierung angeben, sonst hängt das von äusseren Umständen ab ob das klappt oder nicht.

Im Python-Installationsverzeichnis haben eigene Dateien nichts zu suchen. Normalerweise sollte man als Benutzer nicht einmal die Rechte haben dort Dateien zu schreiben‽

Warum steht vor `connection`, `cursor`, und `sql` eigentlich ein `e`? Kryptische Präfixe und Abkürzungen sollte man vermeiden. Ebenso einbuchstabige Namen die nicht auf einen Ausdruck beschränkt sind und nummerierte Namen. Der Präfix `my` hat in 99,9% der Fälle keine Bedeutung, ist also überflüssig und kann weg.

``with`` verwendest Du ja bereits — warum nicht bei allen Dateien? Mit `contextlib.closing()` kann man ``with`` auch für die Datenbankverbindung und den Cursor verwenden. Die Verbindung wird bei Dir ausserdem momentan nicht geschlossen, dafür müsste man die `close()`-Methode auch tatsächlich *aufrufen*.

Die Kommentare sind alle überflüssig. Die beschreiben nur was der Code ganz offensichtlich tut. Kommentare sollten einen Mehrwert gegenüber dem Code liefern. Faustregel: Nicht kommentieren *was* der Code tut, denn das steht ja schon als Code da, sondern *warum* er das (so) tut. Sofern das nicht offensichtlich ist.

Zeichenketten und Werte mit ``+`` und `str()` zusammenstückeln ist eher BASIC als Python. Python kennt dafür Zeichenkettenformatierung per `format()`-Methode auf Zeichenketten. Damit kann man auch `datetime`-Objekte entsprechend formatieren.
„All religions are the same:
religion is basically guilt, with different holidays.” — Cathy Ladman
crazyfile
User
Beiträge: 2
Registriert: Mittwoch 19. April 2017, 11:00

Re: Query in csv-schreiben und Umlaute ersetzen

Beitragvon crazyfile » Mittwoch 19. April 2017, 13:07

BlackJack hat geschrieben:@crazyfile: Ich würde darauf tippen das die Kodierung der Datei nicht mit der Systemkodierung übereinstimmt.

Wenn man Textdateien als Text einliest, sollte man immer explizit die Kodierung angeben, sonst hängt das von äusseren Umständen ab ob das klappt oder nicht.

Das war's! Hab an eine solche Fehlerquelle überhaupt nicht gedacht, da dies ja "nur" die Query ist. Danke!

BlackJack hat geschrieben:@crazyfile:Im Python-Installationsverzeichnis haben eigene Dateien nichts zu suchen. Normalerweise sollte man als Benutzer nicht einmal die Rechte haben dort Dateien zu schreiben‽

Jupp. Dateien und Skripte liegen auch woanders. Wollte nur die realen Verzeichnisse verschleiern, bzw. kürzen.

BlackJack hat geschrieben:@crazyfile:Warum steht vor `connection`, `cursor`, und `sql` eigentlich ein `e`? Kryptische Präfixe und Abkürzungen sollte man vermeiden. Ebenso einbuchstabige Namen die nicht auf einen Ausdruck beschränkt sind und nummerierte Namen. Der Präfix `my` hat in 99,9% der Fälle keine Bedeutung, ist also überflüssig und kann weg.

Präfixe sind für mich zur Übersichtlichkeit. Da ich mehrere Datenbankverbindungen aufbaue in verschiedenen Skripten, haben die cursor bei mir einen Präfix zur einheitlichen Differenzierung.

BlackJack hat geschrieben:@crazyfile:``with`` verwendest Du ja bereits — warum nicht bei allen Dateien? Mit `contextlib.closing()` kann man ``with`` auch für die Datenbankverbindung und den Cursor verwenden. Die Verbindung wird bei Dir ausserdem momentan nicht geschlossen, dafür müsste man die `close()`-Methode auch tatsächlich *aufrufen*.

Die Anwendung von 'with' ist bei mir tatsächlich stiefmütterlich behandelt. Asche auf mein Haupt. Klammern beim close sind dem copy/paste-Monster zum Opfer gefallen. :shock: :roll:

BlackJack hat geschrieben:@crazyfile:Die Kommentare sind alle überflüssig. Die beschreiben nur was der Code ganz offensichtlich tut. Kommentare sollten einen Mehrwert gegenüber dem Code liefern. Faustregel: Nicht kommentieren *was* der Code tut, denn das steht ja schon als Code da, sondern *warum* er das (so) tut. Sofern das nicht offensichtlich ist.

Ok. Diese Struktur gibt mir ein wenig persönlich mehr Übersicht. Denke das ist auch Sache des Geschmacks.

BlackJack hat geschrieben:@crazyfile:Zeichenketten und Werte mit ``+`` und `str()` zusammenstückeln ist eher BASIC als Python. Python kennt dafür Zeichenkettenformatierung per `format()`-Methode auf Zeichenketten. Damit kann man auch `datetime`-Objekte entsprechend formatieren.

Ich bringe mir Python selbst bei aus unterschiedlichen Quellen. Und das "+" ist sehr weit verbreitet. Habe mich aber eben schnell in 'format()' eingelesen und werde das ändern! Ist angenehmer.
Benutzeravatar
BlackJack
Moderator
Beiträge: 31901
Registriert: Dienstag 25. Januar 2005, 23:29
Wohnort: Berlin
Kontaktdaten:

Re: Query in csv-schreiben und Umlaute ersetzen

Beitragvon BlackJack » Freitag 21. April 2017, 13:54

@crazyfile: Den tatsächlichen Pfad zu den Dateien könntest Du wesentlich leichter verschleiern wenn der gemeinsame Präfix nicht mehrfach im Programm stehen würde, sondern am Anfang als Konstante definiert wird. Dann kann man das auch wesentlich einfacher ändern.

Das mit den Präfixen verstehe ich nicht. Die Verbindungen stehen ja nicht im gleichen Namensraum, also kann es da auch keine Verwechslung geben. Wenn man Präfixe für die Übersichtlichkeit braucht, dann hat man zu viele Namen im gleichen Gültigkeitsbereich.

Kommentare statt guter Namen ist keine Geschmacksfrage und das offensichtliche zu kommentieren auch nicht wirklich, denn das verwirrt Leser nur, die sich dann fragen warum der Kommentar dort steht der nicht wirklich etwas erklärt. Und am schlimmsten sind Kommentare die nicht mit dem Code übereinstimmen, was bei der Weiterentwicklung gerne mal passiert. Oder wenn man Fehler gemacht hat. Und dann sitzt man da und muss überlegen wer Recht hat — der Code oder Kommentar. Es ist letztlich das gleiche Problem von Redundanz wie bei Code und Daten.

Ich lande da ungefähr bei so etwas (ungetestet):
  1. import csv
  2. import os
  3. from contextlib import closing
  4. from datetime import datetime as DateTime
  5.  
  6. import cx_Oracle
  7.  
  8. ENCODING = 'utf8'
  9. BASE_PATH = 'C:/Python34'
  10. DB_CREDENTIALS = 'user/Passwort@Datenbank'
  11.  
  12.  
  13. def load_file(filename, encoding=ENCODING):
  14.     with open(filename, encoding=encoding) as file_:
  15.         return file_.read()    
  16.  
  17.  
  18. def query_db(credentials, sql_query):
  19.     with closing(cx_Oracle.connect(credentials)) as connection:
  20.         with closing(connection.cursor()) as cursor:
  21.             cursor.execute(sql_query)
  22.             rows = cursor.fetchall()
  23.             headers = [c[0] for c in cursor.description]
  24.             return headers, rows
  25.  
  26.  
  27. def save_csv(filename, headers, rows, encoding=ENCODING):
  28.     with open(filename, 'w', encoding=encoding) as csv_file:
  29.         writer = csv.writer(
  30.             csv_file,
  31.             delimiter=';',
  32.             lineterminator='\n',
  33.             quoting=csv.QUOTE_NONNUMERIC,
  34.         )
  35.         writer.writerow(headers)
  36.         writer.writerows(rows)
  37.  
  38.  
  39. def main():
  40.     sql_query = load_file(os.path.join(BASE_PATH, 'Scripts', 'user2.sql'))
  41.     headers, rows = query_db(DB_CREDENTIALS, sql_query)
  42.     print('Es wurden', len(rows), 'Datensätze gefunden.')
  43.     filename = '{0:%Y%m%d%H%M%S}_Userdaten.csv'.format(DateTime.now())
  44.     save_csv(os.path.join(BASE_PATH, filename), headers, rows)
  45.  
  46.  
  47. if __name__ == '__main__':
  48.     main()
„All religions are the same:
religion is basically guilt, with different holidays.” — Cathy Ladman

Zurück zu „Datenbankprogrammierung mit Python“

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder