Python sqlite3编程错误:除非您使用可以解释8位字节串的text_factory,否则不得使用8位字节串。

4

我正在编写一个脚本,它可以递归地扫描目录并将其存储在字典中,该字典是列表的集合。这些列表包含文件名和文件大小的列表。如下所示,这个文件名可能包含UTF-8字符。

['test.rus (\xd0\xa5\xd0\xb5\xd0\xbb\xd1\x8c\xd1\x88\xd0\xb8).srt', 23930]
test.rus (Хельши).srt

现在,当我尝试将该数据插入数据库时,出现以下错误。
Traceback (most recent call last):
  File "filedup.py", line 267, in <module>
    read_file_directory(directory)
  File "filedup.py", line 118, in read_file_directory
    (values[i][0], each, values[i][1]))
sqlite3.ProgrammingError: You must not use 8-bit bytestrings unless you use a text_factory that can interpret 8-bit bytestrings (like text_factory = str). It is highly recommended that you instead just switch your application to Unicode strings.

这个操作的函数如下所示:
from collections import defaultdict
dirDict = defaultdict(list)    
def read_file_directory(path):
    global dirDict
    logger.debug("Path being scanned %s" %path)
    fileStats = []
    for root, subFolders, files in os.walk(path):
        for file_name in files:
            fileStats = []
            fileStats.insert(0, file_name)
            fileSize = os.path.getsize(os.path.join(root,file_name))
            fileStats.insert(1, fileSize)
            dirDict[root].append(fileStats)
    #Insert the data in DB
    cursor = dbHandler.cursor()
    keys = dirDict.keys()
    for each in keys:
        values = dirDict[each]
        print values
        for i in xrange(len(values)):
            print values[i]
            print values[i][0]
            print values[i][1]
            fileName = values[i][0]
            fileSize = values[i][1]
            cursor.execute("insert or ignore into master \
                (FileName, FilePath, FileSize) values(?,?,?)", \
                (values[i][0], each, values[i][1]))
            logger.debug("Insert data for %s, %s, %s" %(values[i][0], each, values[i][1]))

我现在正在学习Python,但是我不知道如何解决这个问题。下面是我正在使用的Python版本。

$ python
Python 2.7.6 (default, Mar 22 2014, 22:59:56) 
[GCC 4.8.2] on linux2

有什么想法可以修复当前版本的Python,因为我正在寻找通用解决方案,以便即使在更高版本上也可以工作。 此外,我观察到由于此错误,没有任何数据插入到数据库中。那么如何确保即使某些操作导致错误,之前的数据也可以插入到数据库中。


你使用UTF-8而不是unicode有什么特别的原因吗? - Ignacio Vazquez-Abrams
没有理由我不能使用Unicode,而且也不会出现任何问题。 - Abhinav
与您实际问题无关,但是global dirDict不是好的编程风格。最好将dirDict作为参数传递到函数中,这样它的签名就变成了def read_file_directory(path, dirDict),或者如果在调用read_file_directory()之前不需要dirDict,则可以在read_file_directory()中创建它并从该函数返回。 - mhawke
@mhawke,谢谢,我会进行更新。但是一个快速的问题是,如果我想在多个地方使用dirDict,那么引用是否可以正常工作而不出现任何错误?抱歉,如果这是初学者的问题,因为我仍在学习Python。 - Abhinav
在这种情况下,只需在read_file_directory()中创建dirDict并返回它。调用该函数的代码将是这样的:dirDict = read_file_directory('path/path/path') - mhawke
2个回答

4
sqlite 异常建议您切换到 Unicode 字符串,因此您应该这样做。
Python 的目录列表函数(例如 os.walk)具有一个 奇特的属性;当给定普通字符串时,它们将返回普通字符串,并在给定 Unicode 字符串时返回 Unicode 字符串。因此,在像您的代码中使用 os.walk(path) 时,您应确保 path 是 Unicode 字符串。
为此,您可以使用 unicode() 函数显式转换为 Unicode,例如通过在调用 os.walk 之前编写 path = unicode(path)
此外,您需要在代码中调用 cursor.commit() 来实际写入数据库。在循环遍历所有文件名后调用一次应该就足够了。

我该如何确保路径是Unicode字符串。同时,我能够使用以下代码继续前进:cursor.execute("insert or ignore into master \ (FileName, FilePath, FileSize) values(?,?,?)", \ (values[i][0].decode('utf-8'), each.decode('utf-8'), values[i][1]))但我想将其转换为Unicode,以便将来不会出现其他错误。 - Abhinav
@Abhinav 这要看情况。你是怎么调用这个函数的? - parchment
函数调用为 read_file_directory(directory) 目录将作为脚本参数之一传递。 - Abhinav

4
尝试更改以下行:

fileStats.insert(0, file_name)

to

fileStats.insert(0, file_name.decode('utf8'))

我已经用以下方式完成了 cursor.execute("insert or ignore into master \ (FileName, FilePath, FileSize) values(?,?,?)", \ (values[i][0].decode('utf-8'), each.decode('utf-8'), values[i][1])),所以它可以正常工作。 - Abhinav
@Abhinav 可能会起作用,但最好尽早转换为Unicode,这样你就可以在内部处理Unicode了。@parchment 关于将Unicode路径用作 os.walk() 的参数可能有一个好点子- 你也应该检查一下。 - mhawke
好的,我已经更新了代码,如下:path = unicode(directory) read_file_directory(path),并且它可以正常工作,而不需要使用decode('utf-8')方法。所有的值都能够被成功插入到数据库中,没有出现任何错误。 - Abhinav
@Abhinav:虽然文档没有明确提到 os.walk(),但我已经尝试使用 Unicode 路径参数来调用 os.walk,并且它可以像 parchment 描述的那样工作。这可能是解决问题最干净的方法。 - mhawke
@Abhinav - 哦,好的。 - mhawke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接