sqlite3.ProgrammingError: Você não deve usar cadeias de bytes de 8 bits, a menos que use uma text_factory que pode interpretar cadeias de bytes de 8 bits


90

Usando SQLite3 em Python, estou tentando armazenar uma versão compactada de um fragmento de código HTML UTF-8.

O código é parecido com este:

...
c = connection.cursor()
c.execute('create table blah (cid integer primary key,html blob)')
...
c.execute('insert or ignore into blah values (?, ?)',(cid, zlib.compress(html)))

Em que ponto em obter o erro:

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.

Se eu usar 'texto' em vez de 'blob' e não compactar o trecho de HTML, tudo funciona bem (db é muito grande). Quando eu uso 'blob' e faço compactação por meio da biblioteca Python zlib, recebo a mensagem de erro acima. Olhei em volta, mas não consegui encontrar uma resposta simples para essa pergunta.

Respostas:


94

Se você quiser usar strings de 8 bits em vez de strings Unicode no sqlite3, defina o text_factory apropriado para a conexão sqlite:

connection = sqlite3.connect(...)
connection.text_factory = str

7
Isso pode causar problemas com codificações diferentes, já que você ainda está tentando analisar dados binários como texto. É melhor usar sqlite3.Binary.
MarioVilas

35

Encontrei a solução, deveria ter gasto um pouco mais de tempo pesquisando.

A solução é 'lançar' o valor como um 'buffer' Python, assim:

c.execute('insert or ignore into blah values (?, ?)',(cid, buffer(zlib.compress(html))))

Espero que isso ajude mais alguém.


1
Quando fiz isso, meu banco de dados estava cheio de texto base36, o que tornaria o banco de dados maior do que armazenar o blob diretamente.
Brian Minton

3
Isso está incorreto, você deve usar sqlite3.Binary ao invés, como diz a documentação.
MarioVilas

Parece que sqlite3.Binary () é simplesmente um alias de buffer (), pelo menos a partir de github.com/ghaering/pysqlite/blob/master/lib/dbapi2.py#L54
stevegt

Hã. E também parece que esta seção da documentação do pysqlite realmente incentiva o uso de buffer (): "Os seguintes tipos de Python podem ser enviados para SQLite sem nenhum problema: ..." [tipo Python] buffer ... [tipo SQLite] BLOB " docs.python.org/2/library/sqlite3.html#introduction
stevegt

35

Para trabalhar com o tipo BLOB, você deve primeiro converter sua string compactada zlib em dados binários - caso contrário, o sqlite tentará processá-la como uma string de texto. Isso é feito com sqlite3.Binary (). Por exemplo:

c.execute('insert or ignore into blah values (?, ?)',(cid, 
sqlite3.Binary(zlib.compress(html))))

isso funciona. No entanto, eu queria saber por que isso é necessário. O tipo "BLOB" já indicava que os dados nesta coluna são binários? Observe que no Python 2 a string pode ser texto ou binária. O sqlite3 não deveria apenas tratar o objeto (string compactada zlib) como binário para o tipo BLOB?
user1783732

Não acho que o Python tenha todo o esquema do banco de dados na memória para consultar os tipos de dados corretos - provavelmente ele apenas adivinha os tipos em tempo de execução com base no que você passa, portanto, uma string binária não pode ser diferenciada de uma string de texto.
MarioVilas

Como o SQLite usa o tipo dinâmico: sqlite.org/datatype3.html @ user1783732
Lester Cheung

1

Sintaxe:

5 tipos de armazenamento possíveis: NULL, INTEGER, TEXT, REAL e BLOB

BLOB é geralmente usado para armazenar modelos em conserva ou modelos em conserva de endro

> cur.execute('''INSERT INTO Tablename(Col1, Col2, Col3, Col4) VALUES(?,?,?,?)''', 
                                      [TextValue, Real_Value, Buffer(model), sqlite3.Binary(model2)])
> conn.commit()

> # Read Data:
> df = pd.read_sql('SELECT * FROM Model, con=conn) 
> model1 = str(df['Col3'].values[0]))
> model2 = str(df['Col'].values[0]))

0

Você pode armazenar o valor usando repr (html) em vez da saída bruta e, em seguida, usar eval (html) ao recuperar o valor para uso.

c.execute('insert or ignore into blah values (?, ?)',(1, repr(zlib.compress(html))))

1
Usar eval e repr assim é muito sujo. Não importa o quanto você confia em uma fonte de dados.
Jason Fried

Eu concordo, qualquer coisa é melhor do que eval () aqui. A solução certa é usar sqlite3.Binary, mas se você não puder por algum motivo, é melhor codificar os dados de uma maneira mais segura - por exemplo, com base64.
MarioVilas
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.