#python #sql-server #bittorrent #pyodbc
#python #sql-сервер #bittorrent #pyodbc
Вопрос:
Я работаю над проектом с участием BitTorrent, где я получаю битовое поле в виде строки python. Например:
битовое поле = «000001110100111000110101100010»
Я хотел бы иметь возможность преобразовать строку python в формат, чтобы ее можно было вставить как есть в столбец varbinary (max) базы данных MSSQL с использованием PYODBC. Если я попытаюсь вставить его в виде строки как есть, он, конечно, пожалуется на ошибку незаконного преобразования.
Обратите внимание, что PYODBC ожидает массив байтов или буфер в качестве входных данных для поля переменной в соответствии с их документацией.
Любые предложения будут оценены.
Ответ №1:
Предполагая, что вы используете последнюю версию 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 также доступна в том же месте.
Комментарии:
1. Есть ли какой-нибудь способ сохранить лидирующие 0? Поскольку это битовое поле, они важны.
2. Если вы заранее знаете, сколько битов должно быть, вы можете использовать форматирование строки python, чтобы заполнить строку нулями. Например, если должно быть 30 бит, то вы могли бы заменить последнюю строку в приведенном выше примере кода на
"{:030b}".format(struct.unpack(">I", result[0])[0])
что должно привести к'000001110100111000110101100010'
.
Ответ №2:
Прежде чем я перейду к кодированию, я хотел бы дать одну рекомендацию: значение ‘bitfield’ не является длиной, которую можно разделить на байты. Я бы посоветовал, чтобы всякий раз, когда вы имеете дело с битовыми строками, вы увеличивали их размеры в байтах (например, если len(bitfield)%8 != 0: print ‘Убедитесь, что битовое поле может быть полностью представлено байтами!’), Чтобы гарантировать отсутствие двусмысленности в том, как поля обрабатываются на разных языках программирования, в разных библиотеках в языках программирования и в разных базах данных. Другими словами, база данных, python, библиотека, которую я собираюсь рекомендовать, и т.д., Все будут либо хранить, либо смогут представлять этот битовый массив в виде массива байтов. Если предоставленный bitarray не разделяется равномерно на байты, произойдет одно из трех событий: 1) Будет выдана ошибка (это оптимистично) 2) Bitarray автоматически останется дополненным. 3) Битовый массив будет автоматически волшебным образом дополнен.
Я бы рекомендовал использовать какую-нибудь библиотеку битовых строк. Для этой цели я использовал 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 '2d (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 'x01xd3x8db'
conn.rollback()
results_int = int(hexlify(results),16)
print '2d (bitfield -> BitArray -> int -> DB (where data is binary packed) -> unpacked with hexlify -> int)' % results_int
print '2s (Original bitfield)' % bitfield
from_db_using_ba_hexlify_and_int_with_length = BitArray(int=int(hexlify(results),16), length=30).bin
print '2s (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 '2s (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's next.
print '2s (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 '2s (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 '2s (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 '2s (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 '2s (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: 'x01xd3x8db'
Обратите внимание, что, поскольку у вас нет битовой строки, которую можно напрямую разбить на байты (это строка, представляющая 30 бит), единственным способом получить точно такую же строку было указать длину, и даже тогда результаты не были согласованными в зависимости от того, как был создан экземпляр BitArray.
Комментарии:
1. Последнее предостережение — я пытался использовать pymssql для хранения IP-адресов в переменных полях. Если передать int (как это сделано выше в srgerg’s и моем сообщении) в запрос INSERT, можно столкнуться с проблемами, если длина > 31 бита (для python int первый бит хранит знак, остальные биты могут хранить значение, поэтому использование всех 32 бит может привести к отправке в базу данных неожиданных целых значений). Чтобы избежать проблем, сначала упакуйте целое число и передайте двоичное упакованное значение в инструкции ‘INSERT’, вместо того, чтобы полагаться на библиотеку для преобразования int в упакованный двоичный файл.