Преобразование шестнадцатеричного в двоичный в виде строки

Этот код возвращает двоичную строку в 4-значной форме для данного шестнадцатеричного кода в виде строки.

def str_bin_in_4digits(aString):
    retStr=""
    for i in aString:
        retStr = retStr+"{0:04b}".format(int(i, 16))+" "
    return retStr.strip()

Например,

>>> str_bin_in_4digits("20AC")
0010 0000 1010 1100

Код работает, как ожидалось, и меня беспокоит, может ли он быть более элегантным, например, быстрее или с меньшим потреблением памяти?

3 ответа
3

Проблема состоит в том, чтобы преобразовать шестнадцатеричную строку в эквивалентную двоичную строку. Каждый символ во входной строке отображается на один или несколько символов в выходной строке, например, ‘0’ -> ‘0000’, … ‘A’ -> ‘1010’, … ‘F’ -> ‘ 1111 ‘. Это идеально подходит для string.translate()

table = {'0':'0000', '1':'0001', '2':'0010', '3':'0011',
         '4':'0100', '5':'0101', '6':'0110', '7':'0111',
         '8':'1000', '9':'1001', 'A':'1010', 'B':'1011',
         'C':'1100', 'D':'1101', 'E':'1110', 'F':'1111'}


def str_bin_in_4digits(hex_string):
    return hex_string.upper().translate(table)

Это в 6-7 раз быстрее.

    Я не знаю, есть ли способ потребления памяти быстрее или меньше, но вы можете написать его более элегантно, используя список

    def str_bin_in_4digits(aString):
    
        data = []
    
        for i in aString:
            data.append( "{0:04b}".format(int(i, 16)) )
    
        retStr = " ".join(data)
    
        return retStr  # doesn't need strip()
    

    а затем вы можете написать это также как понимание списка

    def str_bin_in_4digits(aString):
    
        data = ["{0:04b}".format(int(i, 16)) for i in aString]
        retStr = " ".join(data)
    
        return retStr  # doesn't need strip()
    

    а затем вы можете даже сократить до одной строки

    def str_bin_in_4digits(aString):
    
        return " ".join(["{0:04b}".format(int(i, 16)) for i in aString])
    

    Возможно, даже при использовании понимания списка это может быть немного быстрее, но для небольшой строки вы можете этого не увидеть.

    Теперь вам нужно выбрать только наиболее читаемую версию.


    Проблема в том, что у вас есть 4-bits values (одинарный шестнадцатеричный символ).

    Для полного 8-bits вы можете преобразовать из шестнадцатеричного в байты, используя

     bytes_data = bytes.fromhex('0A 0B 0C')
    
     bytes_data = bytes.fromhex('0A0B0C')
    

    а позже вы снова можете использовать понимание списка — используя f-string это могло быть даже короче

     data = [f'{x:04b}' for x in bytes_data]
    

    Но с 8-bits вам придется разделить его на 4-bits используя что-то вроде этого

    bytes_data[0] >> 4,  bytes_data[0] & 0x0f
    

    • 1

      Если вы считаете, что этот вопрос не по теме, зачем на него отвечать?

      — Райндериен

    • @Reinderien, потому что мне нравится решать проблемы. И эта проблема мне кажется интересной. Но обычно я делаю это на Stackoverflow.

      — легкий


    • пожалуйста, избегайте использования camelCasing для переменных в питоне 🙂

      — hjpotter92

    • @ hjpotter92 OP используется aString поэтому я решил оставить его и не добавлять в ответ PEP 8 — Руководство по стилю кода Python

      — легкий

    Рассмотрение

    У вас не так много кода для проверки, поэтому он обязательно будет коротким.

    • PEP-8: Руководство по стилю кода Python рекомендует:
      • snake_case для функций, переменных и параметров. Так aString должно быть a_string, а также retVal должно быть ret_val.
    • Лучшие имена параметров
      • Что такое aString? "Hello World" является строкой, но мы не можем ее использовать, потому что на самом деле вы ожидаете шестнадцатеричную строку. Возможно hex_string было бы лучшим именем параметра.
      • По аналогии, binary_string было бы более наглядным, чем retStr.
    • А '''docstring''' было бы полезно для функции.
    • Подсказки типа также были бы полезны.

    Альтернативная реализация

    Посимвольное выполнение действий неэффективно. Обычно гораздо быстрее позволить Python выполнять работу самому с его эффективными, оптимизированными функциями нативного кода.

    Форматирование строк Python поддерживает добавление разделителя запятой между тысячами групп.

    >>> f"{123456789:,d}"
    '123,456,789'
    

    Он также поддерживает добавление подчеркивания между группами из 4 цифр при использовании кодов двоичного или шестнадцатеричного формата:

    >>> f"{548151468:_x}"
    '20ac_20ac'
    >>> f"{0x20AC:_b}"
    '10_0000_1010_1100'
    

    Это большая часть пути к тому, что вы ищете. Просто нужно превратить символы подчеркивания в пробелы с помощью .replace(...) и заполните начальными нулями, добавив к строке формата флаг ширины и нулевого заполнения.

    def str_bin_in_4digits(hex_string: str) -> str:
        """
        Turn a hex string into a binary string.
        In the output string, binary digits are space separated in groups of 4.
    
        >>> str_bin_in_4digits('20AC')
        '0010 0000 1010 1100'
        """
    
        value = int(hex_string, 16)
        width = len(hex_string) * 5 - 1
        bin_string = f"{value:0{width}_b}"
        return bin_string.replace('_', ' ')
    
    if __name__ == '__main__':
        import doctest
        doctest.testmod(verbose=True)
    

    В зависимости от вашего определения элегантного, вы можете сделать это в одну строку:

    def str_bin_in_4digits(hex_string: str) -> str:
        """
        Turn a hex string into a binary string.
        In the output string, binary digits are space separated in groups of 4.
    
        >>> str_bin_in_4digits('20AC')
        '0010 0000 1010 1100'
        """
    
        return f"{int(hex_string,16):0{len(hex_string)*5-1}_b}".replace('_', ' ')
    

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *