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.