Python PostgreSQL 数据库的编码问题

Python encoding problems with PostgreSQL Database

我正在开发一个程序,我想将从 csv 文件读取的地址与 postgres 数据库进行比较。 (它是 QGis 的插件) 我可以成功建立连接,也可以从数据库中读取数据,只要我不带自己的参数发送查询。

所以我是怎么做的: 我读取了一个 csv 文件并将其存储在列表中。 然后我select一个输出文件。 接下来,我单击一个按钮,该按钮在单击时应将 csv 文件中的条目与我的数据库中的条目进行比较。 如果 csv 文件中的条目(邮政编码、城镇、地址)在数据库中具有完全相同的属性,我将其写入列表 "Successful Matches",如果不匹配,我将其写入列表“错误列表” )

当我使用自己的参数执行语句时,我的问题出现了。 我收到的 Sql 错误消息说:

Invalid Byte-Sequence for Encoding UTF8: 0xdf 0x65

我认为错误出在我从 csv 文件中填写的第一个列表中。我的地址有特殊字符,例如 öäüß...

这里是使用的代码:

This Method writes the succesfully matched addresses to a file, the failed ones to a lineEdit

def write_output_file(self):
    compare_input_with_database()
    try:
        with open(self.outputfile, 'wb') as csvfile:
            writer = csv.writer(csvfile, delimiter=';', quoting=csv.QUOTE_MINIMAL)
            for row in geocoded_list:
                writer.writerow(row)
        if len(error_list) > 0:
            self.writefailedaddresses()
            raiseInformation("Es konnten nicht alle Adressen geocodiert werden!")
        else:
            raiseInformation("Adressen erfolgreich geocodiert!")
    except csv.Error:
        raiseException("Fehler beim schreiben der Datei")



This method, compares a row entry from the list/csvfile to the database.


def compare_input_with_database():
dbcursor = database_connection.open_connection()
for row in addressList:
    entry = str(row[0])
    addresssplit = entry.split(';')
    try:
        resultset = database_connection.select_specific_address(dbcursor, int(addresssplit[0]), addresssplit[1], addresssplit[2])
        geocoded_list.append(resultset)
    except psycopg2.DatabaseError, e:
        raiseException(e)
        error_list.append(addresssplit)
database_connection.close_connection()

def select_specific_address(cursor, plz, town, address):
cursor.execute("SELECT plz,ort,strasse,breitengrad,laengengrad from addresses where plz=%s AND ort=%s AND strasse=%s", (plz, town, address))
resultset = cursor.fetchone()
return resultset



This Method reads a csv file and populates it in a list

def loadFileToList(addressfile, dlg):
del addressList[:]
if os.path.exists(addressfile):
    if file_is_empty(addressfile):
        raiseException("Ungueltige Quelldatei! Quelldatei ist leer!")
        return -1
    else:
        with open(addressfile, 'rb') as csvfile:
            filereader = csv.reader(csvfile, delimiter=';')
            for row in filereader:
                addressList.append(row)
        return addressList
else:
    raiseException("Pfad der Quelldatei nicht gefunden!")
    return -1

谢谢!

编辑: 当我显示包含特殊字符的地址时,它显示为 "Hauptstra\xdfe" 而不是“Hauptstraße 抱歉,我的编码不好,这是 unicode 吗? 这是否意味着它会像那样发送到数据库,我需要以不同的方式对其进行编码?

编辑 2: 我看了一下 orkaround 并尝试实现它:

def loadFileToList(addressfile, dlg):
del addressList[:]
if os.path.exists(addressfile):
    if file_is_empty(addressfile):
        raiseException("Ungueltige Quelldatei! Quelldatei ist leer!")
        return -1
    else:
        #with open(addressfile, 'rb') as csvfile:
            #filereader = csv.reader(csvfile, delimiter=';')
        reader = unicode_csv_reader(open(addressfile))
        for row in reader:
            addressList.append(row)
        return addressList
else:
    raiseException("Pfad der Quelldatei nicht gefunden!")
    return -1


def unicode_csv_reader(utf8_data, dialect=csv.excel, **kwargs):
csv_reader = csv.reader(utf8_data, dialect=dialect, **kwargs)
for row in csv_reader:
    yield [unicode(cell, 'utf-8') for cell in row]

但是现在我在执行代码时收到以下错误消息: 对于 reader 中的行:

File "C:/Users/Constantin/.qgis2/python/plugins\Geocoder\logic.py", line 46, in unicode_csv_reader yield [unicode(cell, 'utf-8') for cell in row] UnicodeDecodeError: 'utf8' codec can't decode byte 0xdf in position 19: invalid continuation byte

我只是不明白为什么它不能解码它-.-

更新:

我的 csv 文件中的一些行:

1190;Wien;Weinberggasse
1190;Wien;Hauptstraße
1190;Wien;Kärnterstraße

except 的语法暗示您正在使用 Python 2。但是由于您在字符串中使用非 ASCII (Unicode) 字符,因此 Python 3 是一个显着的更好的选择。你似乎在用德语工作,所以至少有几个 ü 和 ß 会潜入。

在Python3中,读数正好是:

rows = []
with open('test.csv', encoding='utf-8') as csvfile:
    reader = csv.reader(csvfile, delimiter=';', quoting=csv.QUOTE_MINIMAL)
    for row in reader:
        rows.append(row)

并写作:

with open('testout.csv', mode="w", encoding='utf-8') as csvfile:
    writer = csv.writer(csvfile, delimiter=';', quoting=csv.QUOTE_MINIMAL)
    for row in rows:
        writer.writerow(row)

注意read/write不是二进制的,编码是理所当然的处理。结果正是您所期望的。类似于:

Artikelname;Menge
Äpfel;3
Bäume;12

所有字符都正确编码。在磁盘上,数据采用 UTF-8 编码。在内存中,完整的 Unicode。

如果你不能使用Python 3,那么你必须非常注意Unicode字符被正确编码和解码,尤其是在I/O边界——例如在读取和写入数据,或与 PostgreSQL 等外部系统通信时。目前的代码至少有几种方式没有注意。例如,使用 str() 转换不一定是 ASCII 字符的内容,以及缺少 UTF-8 编码。

不幸的是,Python 2 中没有简单的修复。Python 2 的 CSV 模块是 schrecklich kaput。来自 the docs: "The csv module doesn’t directly support reading and writing Unicode." In 2016?! There are, however, workarounds. One of the recipes is right there in the docs. This Stack Overflow answer 重申,并提供了其他几种选择。

因此,如果可以,请使用 Python 3。它将简化这个问题和许多其他非 ASCII I/O 问题。否则,在其他 SO 答案中部署 CSV 解决方法之一。

更新

如果您在使用解决方法时遇到问题,我也不喜欢标准答案。这是一个对我有用的非规范 reader 解决方案:

import csv

rows = []
with open('test.csv', mode='rb') as csvfile:
    reader = csv.reader(csvfile, delimiter=';', quoting=csv.QUOTE_MINIMAL)
    for row in reader:
        urow = [unicode(cell, 'utf-8') for cell in row]
        rows.append(urow)

print rows

这绝对不能移植到 Python 3,但它可以工作并且不需要导入任何其他模块。请注意,如果您使用 IPython/Jupyter 或交互式 Python 控制台 ("REPL"),您将看到低级别的字符串,例如:

[ [u'Artikelname', u'Menge'], 
  [u'\xc4pfel', u'3'], 
  [u'B\xe4ume', u'12] 
]

所以字符串没有漂亮、整洁的 Ä,而是 \xc4。这很烦人,但这并没有错。输入 u'\u00c4pfel' 得到同样的结果。而且很容易确认 c4the correct Unicode code point。 Python 2 在处理非 ASCII 字符方面做得很差。尽可能使用 Python3 的 4,094 个理由之一。

手动输出,顺便说一句:

with open('testout.csv', mode='wb') as csvfile:
    writer = csv.writer(csvfile, delimiter=';', quoting=csv.QUOTE_MINIMAL)
    for row in rows:
        urow = [cell.encode('utf-8') for cell in row]
        writer.writerow(urow)

所有这一切完全取决于您的输入文件真正采用 UTF-8 编码。如果它们在其他任何地方,那将打开另一个可怕的腐烂鱼罐。