Преобразование двоичных данных в печатный шестнадцатеричный - PullRequest
1 голос
/ 05 января 2009

В этой ветке кто-то заметил, что следующий код должен использоваться только в «игрушечных» проектах. К сожалению, он не вернулся, чтобы сказать, почему он не имеет производственного качества, поэтому я надеялся, что кто-то в сообществе сможет либо заверить меня, что код в порядке (потому что он мне очень нравится), либо определить, что не так. *

template< class T1, class T2>
void hexascii( T1& out, const T2& in )
{
    out.resize( in.size() * 2 );
    const char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7','8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
    T1::iterator outit = out.begin();
    for( T2::const_iterator it = in.begin(); it != in.end(); ++it )
    {
        *outit++ = hexDigits[*it >> 4];
        *outit++ = hexDigits[*it & 0xF];
    }
}

template<class T1, class T2>
void asciihex( T1& out, const T2& in )
{
    size_t size = in.size;
    assert( !(size % 2) );

    out.resize( size / 2 );
    T1::iterator outit = out.begin();
    for( T2::const_iterator it = in.begin(); it != in.end(); it += 2, ++outit )
    {
    *outit = ((( (*it > '9' ? *it - 0x07 : *it)  - 0x30) << 4) & 0x00f0) + 
                (((*(it+1) > '9' ? *(it+1) - 0x07 : *(it+1)) - 0x30) & 0x000f);
    }
}

Edit: Спасибо за вашу помощь, ребята, вы сделали несколько больших улучшений. Я написал функции в двух предложенных стилях из ваших ответов. Некоторые грубые тесты показывают, что второй метод несколько быстрее, чем первый, но IMO перевешивает улучшенную читаемость первого.

template<class T1>
void asciihex2( T1& out, const std::string& in )
{
    dassert( sizeof(T1::value_type)==1 );
    size_t size = in.size();
assert( !(size % 2) );
    out.resize( size / 2 );
    T1::iterator outit = out.begin();
    for( size_t i = 0; i < in.size(); i += 2 )
    {
        int tmp;
        sscanf( in.c_str() + i, "%02X", &tmp );
        *outit++ = tmp;
    }
}

template<class T1>
void asciihex3( T1& out, const std::string& in )
{
    dassert( sizeof(T1::value_type)==1 );
    size_t size = in.size();
assert( !(size % 2) );
    out.resize( size / 2 );
    T1::iterator outit = out.begin();
const char hexDigits[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 
                          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                  0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
for( std::string::const_iterator it = in.begin(); it != in.end(); it += 2, ++outit )
    {
    *outit = (hexDigits[(*it - 0x30) & 0x1f] << 4) + 
              hexDigits[((*(it+1) - 0x30) & 0x1f)];
    }
}

Некоторые из предположений, дополняющих этот код: 1: они не предназначены для общего использования, но используются в анонимном пространстве имен для перевода данных для определенного класса. 2: Шаблонирование требуется, так как используются два отдельных типа контейнера (один - std :: vector, другой - аналогичный контейнер типа байтового массива из сторонней библиотеки. 3: Цель состоит в том, чтобы иметь возможность преобразовывать двоичные данные неопределенной длины в строки и обратно (0x1234abcd <-> "1234abcd") 4: утвердить ошибки в режимах отладки и выпуска 5: к моменту вызова этих функций размер строки уже будет проверен, assert используется для прекращения обработки, если что-то серьезное пошло не так 6: Нужно прокомментировать

Любые другие идеи приветствуются.

Ответы [ 9 ]

8 голосов
/ 05 января 2009

Кажется, что большого количества шаблонного кода добиться очень мало, учитывая, что у вас есть прямое шестнадцатеричное преобразование в стандартных функциях C scanf и printf . зачем?

6 голосов
/ 05 января 2009

Мой главный комментарий о том, что это очень трудно читать.

Особенно:

*outit = ((( (*it > '9' ? *it - 0x07 : *it)  - 0x30) << 4) & 0x00f0) + 
            (((*(it+1) > '9' ? *(it+1) - 0x07 : *(it+1)) - 0x30) & 0x000f)

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

4 голосов
/ 05 января 2009

Что он должен делать? Не существует общепринятого общепринятого значения hexascii или asciihex, поэтому имена должны измениться.

[править] Преобразование из двоичной в шестнадцатеричную запись часто не следует называть ascii ..., так как ascii является 7-битным форматом.

3 голосов
/ 05 января 2009
  • В коде есть операторы утверждения вместо правильной обработки условия ошибки (и если ваше утверждение отключено, код может взорваться)

  • для цикла имеет опасное двойное увеличение итератора (это + = 2). Особенно в том случае, если ваше заявление не сработало. Что происходит, когда ваш итератор уже в конце, а вы ++ его?

  • Код является шаблоном, но вы просто конвертируете символы в числа или наоборот. Это программирование культа грузов . Вы надеетесь, что с помощью шаблонов вы получите благословение программирования шаблонов. Вы даже пометили это как вопрос шаблона, хотя аспект шаблона совершенно не имеет отношения к вашим функциям.

  • строка * outit = слишком сложна.

  • код заново изобретает колесо. В большом смысле.

3 голосов
/ 05 января 2009

Что не так с

*outit = hexDigits[*it]

Почему эти две функции не могут совместно использовать общий список hexDigits и исключить сложные (и медленные) вычисления символа ASCII?

3 голосов
/ 05 января 2009

Я действительно не против этого. Он универсален (в определенных пределах), использует константы, ссылки, где это необходимо и т. Д. В нем не хватает документации, а назначение asciihex *outit не совсем понятно с первого взгляда.

resize инициализирует ненужные элементы вывода (вместо этого используйте reserve).

Возможно, универсальность слишком гибкая: вы можете задавать алгоритмы любым типом данных, который вам нравится, в то время как вы должны давать ему только шестнадцатеричные числа (не например, vector из double с)

И действительно, это может быть битное превышение , учитывая наличие хороших библиотечных функций.

2 голосов
/ 05 января 2009

Некоторые проблемы, которые я вижу:

Это будет прекрасно работать, если он используется только для входного контейнера, в котором хранятся 8-битные типы - например, char или unsigned char. Например, следующий код завершится ошибкой, если используется с 32-битным типом, значение которого после сдвига вправо больше 15 - рекомендуем всегда использовать маску, чтобы убедиться, что индекс поиска всегда находится в диапазоне.

*outit++ = hexDigits[*it >> 4];

Каково ожидаемое поведение, если вы передадите контейнер, содержащий unsigned long - для того, чтобы он был универсальным классом, он, вероятно, также сможет обрабатывать преобразование 32-битных чисел в строки шестнадцатеричного типа.

Это работает только тогда, когда вход является контейнером - что если я просто хочу преобразовать один байт? Здесь предлагается реорганизовать код в базовую функцию, которая может преобразовать один байт (hex => ascii и ascii => hex), а затем предоставить дополнительные функции для использования этой основной функции для покрытия контейнеров байтов и т. Д.

В asciihex () могут произойти плохие вещи, если размер входного контейнера не делится на 2. Использование:

it != in.end(); it += 2

опасно, так как если размер контейнера не делится на 2, то приращение на два продвинет итератор за конец контейнера, и сравнение с end () никогда не сработает. Это несколько защищено от вызова assert, но assert может быть скомпилирован (например, он часто компилируется в сборках релиза), поэтому было бы намного лучше сделать это оператор if.

1 голос
/ 05 января 2009

Проблемы, которые я замечаю:

hexascii не проверяет, если sizeof(T2::value_type)==1

hexascii разыменовывается it дважды, asciihex еще больше. Нет причин для этого, так как вы можете сохранить результат. Это означает, что вы не можете использовать istream_iterator.

asciihex нужен случайный итератор в качестве входных данных, поскольку используются (it + 1) и (it + = 2). Алгоритм может работать на прямом итераторе, если вы используете только (++ it).

(*it > '9' ? *it - 0x07 : *it) - 0x30 можно упростить до *it - (*it > '9' ? 0x37 : 0x30), поэтому остается только одно безусловное вычитание. Тем не менее, поиск в массиве будет более эффективным. Вычтите 0x30. «0» станет 0, «A» станет 0x11, а «a» станет 0x31. Маскируйте 0x1f, чтобы сделать его нечувствительным к регистру, и вы можете выполнить результирующий поиск в символе [0x20] без риска переполнения. Не шестнадцатеричные символы будут просто давать вам странные значения.

0 голосов
/ 05 января 2009

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...