Вставка битового торрент-поля в VarBinary (MAX) в MSSQL с использованием PYODBC

1

Я работаю над проектом с участием BitTorrent, где я получаю бит-поле в виде строки python. Например:

bitfield = "000001110100111000110101100010"

Я хотел бы иметь возможность преобразовать строку python в формат, чтобы он мог быть вставлен как есть в столбце varbinary (max) базы данных MSSQL с использованием PYODBC. Если я попытаюсь вставить его как строку, как есть, он, конечно, жалуется на ошибку незаконного преобразования.

Примечание. PYODBC ожидает байтовый массив или буфер в качестве входных данных для поля varbinary в соответствии с их документацией.

Мы ценим любые предложения.

Теги:
sql-server
pyodbc
bittorrent

2 ответа

2
Лучший ответ

Предполагая, что вы используете последнюю версию python, вы можете использовать стандартный struct модуль библиотеки и функцию bin. Вот краткий пример:

con = pyodbc.connect("...")
con.execute("CREATE TABLE bin_test ( bin_col varbinary(max) )")
con.execute("INSERT INTO bin_test VALUES (?)",
    (int("000001110100111000110101100010", 2),))
result = con.execute("SELECT * FROM bin_test").fetchone()
bin(struct.unpack(">I", result[0])[0])

Результатом окончательного утверждения является

'0b1110100111000110101100010'

который является исходным битовым полем (с удалением ведущих нулей).

Документацию для модуля struct можно найти на docs.python.org. Документация для функции bin также доступна в том же месте.

  • 0
    Есть ли способ сохранить лидирующие 0? Поскольку это битовое поле, они важны.
  • 2
    Если вы знаете, сколько бит должно быть заблаговременно, вы можете использовать форматирование строки Python, чтобы заполнить строку нулями. Например, если должно быть 30 битов, вы можете заменить последнюю строку в приведенном выше примере кода на "{:030b}".format(struct.unpack(">I", result[0])[0]) что должно привести к '000001110100111000110101100010' .
2

Прежде чем я получу код, я хотел бы сделать одну рекомендацию: значение "битпотока" не является длиной, которую можно разделить на байты. Я бы предположил, что в любое время, когда вы имеете дело с битовыми строками, вы выражаете их в размерах байтов (например, если len (битполе)% 8! = 0: print 'Убедитесь, что битовое поле может быть полностью представлено байтами!'), Чтобы убедитесь, что нет никакой двусмысленности в том, как манипулировать полями на разных языках программирования, в разных библиотеках на языках программирования и в разных базах данных. Другими словами, база данных, python, библиотека, которую я собираюсь рекомендовать, и т.д. Все собираются либо хранить, либо представлять этот битаррей в виде массива байтов. Если предоставленная битаррей не делит равномерно на байты, произойдет одна из трех вещей: 1) Будет поднята ошибка (это оптимистично). 2) битаррей будет автоматически-магически оставлен. 3) битаррей будет автоматически магически правильно дополнен.

Я бы рекомендовал использовать библиотеку bitstring. Для этой цели я использовал python-bitstring. Я не нашел времени, чтобы разобраться с ODBC здесь, но идея в основном такая же, и использует srgerg ответ:

Примеры:

#!/usr/bin/python
import pymssql
from binascii import hexlify
from bitstring import BitArray
dbconninfo = {'host': 'hostname', 'user': 'username', 'password': 'secret', 'database': 'bitexample', 'as_dict': True}
conn = pymssql.connect(**dbconninfo)
cursor = conn.cursor()

bitfield = "000001110100111000110101100010"

ba = BitArray(bin=bitfield)
print '%32d (bitfield -> BitArray -> int)' % ba.int

cursor.execute("CREATE TABLE bin_test (bin_col varbinary(max) )")
cursor.execute("INSERT INTO bin_test values (%s)", (ba.int,))
cursor.execute("SELECT bin_col FROM bin_test")
results = cursor.fetchone()['bin_col'] # results now contains binary packed data '\x01\xd3\x8db'
conn.rollback()
results_int = int(hexlify(results),16)
print '%32d (bitfield -> BitArray -> int -> DB (where data is binary packed) -> unpacked with hexlify -> int)' % results_int

print '%32s (Original bitfield)' % bitfield
from_db_using_ba_hexlify_and_int_with_length = BitArray(int=int(hexlify(results),16), length=30).bin
print '%32s (From DB, decoded with hexlify, using int to instantiate BitArray, specifying length of int as 30 bits, out as bin)' %
from_db_using_ba_hexlify_and_int_with_length
from_db_using_ba_hex = BitArray(hex=hexlify(results)).bin # Can't specify length with hex
print '%32s (From DB, decoded with hexlify, using hex to instantiate BitArray, can not specify length, out as bin)' % from_db_using_ba_hex
from_db_using_ba_bytes_no_length = BitArray(bytes=results).bin # Can specify length with bytes... that next.
print '%32s (From DB, using bytes to instantiate BitArray, no length specified, out as bin)' % from_db_using_ba_bytes_no_length
from_db_using_ba_bytes = BitArray(bytes=results,length=30).bin
print '%32s (From DB, using bytes to instantiate BitArray, specifying length of bytes as 30 bits, out as bin)' % from_db_using_ba_bytes
from_db_using_hexlify_bin = bin(int(hexlify(results),16))
print '%32s (from DB, decoded with hexlify -> int -> bin)' % from_db_using_hexlify_bin
from_db_using_hexlify_bin_ba = BitArray(bin=bin(int(hexlify(results),16))).bin
print '%32s (from DB, decoded with hexlify -> int -> bin -> BitArray instantiated with bin)' % from_db_using_hexlify_bin
from_db_using_bin = bin(int(results,16))
print '%32s (from DB, no decoding done, using bin)' % from_db_using_bin

Результатом этого является:

                        30641506 (bitfield -> BitArray -> int)
                        30641506 (bitfield -> BitArray -> int -> DB (where data is binary packed) -> unpacked with hexlify -> int)
  000001110100111000110101100010 (Original bitfield)
  000001110100111000110101100010 (From DB, decoded with hexlify, using int to instantiate BitArray, specifying length of int as 30 bits, out as bin)
00000001110100111000110101100010 (From DB, decoded with hexlify, using hex to instantiate BitArray, can not specify length, out as bin)
00000001110100111000110101100010 (From DB, using bytes to instantiate BitArray, no length specified, out as bin)
  000000011101001110001101011000 (From DB, using bytes to instantiate BitArray, specifying length of bytes as 30 bits, out as bin)
     0b1110100111000110101100010 (from DB, decoded with hexlify -> int -> bin)
     0b1110100111000110101100010 (from DB, decoded with hexlify -> int -> bin -> BitArray instantiated with bin)
Traceback (most recent call last):
  File "./bitexample.py", line 38, in <module>
    from_db_using_bin = bin(int(results,16))
ValueError: invalid literal for int() with base 16: '\x01\xd3\x8db'

Обратите внимание, что, поскольку у вас нет битовой строки, которая может быть напрямую разбита на байты (это строка, представляющая 30 бит), единственный способ получить ту же строку - указать длину, и даже тогда результаты не были в зависимости от того, как был создан экземпляр BitArray.

  • 0
    Последнее слово предостережения - я пытался использовать pymssql для хранения IP-адресов в полях varbinary. Если передать запрос INSERT (как это было сделано выше в srgerg и моем посте), можно столкнуться с проблемами, если длина> 31 бита (для python int первый бит хранит знак, остальные биты могут хранить значение, поэтому использование всех 32 битов может привести к тому, что неожиданные целочисленные значения будут помещены в базу данных). Чтобы избежать проблем, сначала упаковайте целое число и передайте двоичное упакованное значение в операторе INSERT, а не полагайтесь на библиотеку для преобразования int в упакованный двоичный файл.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню