Query in csv-schreiben und Umlaute ersetzen

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

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.

Code: Alles auswählen

import cx_Oracle
import csv
from datetime import datetime
econnection = cx_Oracle.connect("user/Passwort@Datenbank")

#Datensatzcursor
ecursor = econnection.cursor()

#Datei mit Query öffnen
f1 = open('C:/Python34/Scripts/user2.sql')
esql = f1.read()
f1.close()

#Abfrage ausführen
ecursor.execute(esql)
result = ecursor.fetchall()

print("Es wurden " + str(ecursor.rowcount) + " Datensätze gefunden.")

#Schreiben in csv-Datei
with open('C:/Python34/' + datetime.strftime(datetime.now(),"%Y%m%d%H%M%S") + '_Userdaten.csv','w') as fp:
    myfile = csv.writer(fp,delimiter=';',lineterminator='\n',quoting=csv.QUOTE_NONNUMERIC)
    myfile.writerow([i[0] for i in ecursor.description])
    myfile.writerows(result)

fp.close()

econnection.close
Zuletzt geändert von Anonymous am Mittwoch 19. April 2017, 11:30, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@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.
crazyfile
User
Beiträge: 2
Registriert: Mittwoch 19. April 2017, 11:00

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

@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):

Code: Alles auswählen

import csv
import os
from contextlib import closing
from datetime import datetime as DateTime

import cx_Oracle

ENCODING = 'utf8'
BASE_PATH = 'C:/Python34'
DB_CREDENTIALS = 'user/Passwort@Datenbank'


def load_file(filename, encoding=ENCODING):
    with open(filename, encoding=encoding) as file_:
        return file_.read()    


def query_db(credentials, sql_query):
    with closing(cx_Oracle.connect(credentials)) as connection:
        with closing(connection.cursor()) as cursor:
            cursor.execute(sql_query)
            rows = cursor.fetchall()
            headers = [c[0] for c in cursor.description]
            return headers, rows


def save_csv(filename, headers, rows, encoding=ENCODING):
    with open(filename, 'w', encoding=encoding) as csv_file:
        writer = csv.writer(
            csv_file,
            delimiter=';',
            lineterminator='\n',
            quoting=csv.QUOTE_NONNUMERIC,
        )
        writer.writerow(headers)
        writer.writerows(rows)


def main():
    sql_query = load_file(os.path.join(BASE_PATH, 'Scripts', 'user2.sql'))
    headers, rows = query_db(DB_CREDENTIALS, sql_query)
    print('Es wurden', len(rows), 'Datensätze gefunden.')
    filename = '{0:%Y%m%d%H%M%S}_Userdaten.csv'.format(DateTime.now())
    save_csv(os.path.join(BASE_PATH, filename), headers, rows)


if __name__ == '__main__':
    main()
Antworten