将二进制数据转换为可打印的十六进制

时间:2009-01-05 12:22:27

标签: c++ templates stl

this帖子中,有人评论说下面的代码只能在'玩具'项目中使用。不幸的是,他还没有回来说为什么它不符合生产质量,所以我希望社区中的某个人能够向我保证代码没问题(因为我非常喜欢)或者找出错误。

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);
    }
}

编辑:感谢您的帮助,您已经做了一些重大改进。我从你的答案中写出了两种建议风格的函数。一些粗略的测试表明第二种方法比第一种方法稍微快一点,但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&lt; - &gt;“1234abcd”)   4:在调试和释放模式下断言陷阱错误   5:当调用这些函数时,字符串的大小已经被检查过,断言用于在严重错误的情况下终止处理   6:需要一些评论

赞赏任何其他想法。

9 个答案:

答案 0 :(得分:8)

考虑到在标准C scanf printf 函数中有直接十六进制转换,似乎很多模板化代码实现得很少。为什么要这么麻烦?

答案 1 :(得分:6)

我对它的主要评论是,它很难阅读。

特别是:

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

如果我继承了代码,那么我的大脑会花一点时间来解决这个问题,并惹恼我。

答案 2 :(得分:4)

该怎么办? hexascii或asciihex没有众所周知的公认含义,因此名称应该改变。

[编辑] 从二进制转换为十六进制表示法通常不应被称为ascii ...,因为ascii是一种7位格式。

答案 3 :(得分:3)

我并不反对它。它是通用的(在限制范围内),它使用了consts,在需要的地方使用了引用等等......它缺少一些文档,asciihex *outit赋值乍一看并不是很清楚。

resize初始化输出的元素是不必要的(改为使用reserve)。

也许通用性有点过于灵活:您可以使用您喜欢的任何数据类型提供算法,而您应该只给它十六进制数字(而不是vector double s)

实际上,考虑到良好的库函数,它可能是bit overkill

答案 4 :(得分:3)

有什么问题
*outit = hexDigits[*it]

为什么这两个函数不能共享一个公共的hexDigit列表并消除ASCII字符的复杂(和慢速)计算?

答案 5 :(得分:3)

  • 代码有断言语句而不是正确处理错误条件(如果你的断言被关闭,代码可能会爆炸)

  • for循环具有危险的迭代器双倍增加(它+ = 2)。特别是在你的断言没有开火的情况下。如果你的迭代器已经结束了,那你会发生什么呢?

  • 代码是模板化的,但您所做的只是将字符转换为数字或反过来。这是cargo cult programming。您希望通过使用模板来获得模板编程的祝福。您甚至将此标记为模板问题,尽管模板方面与您的功能完全无关。

  • * outit =行太复杂了。

  • 代码重新发明轮子。在很大程度上。

答案 6 :(得分:2)

我看到的一些问题:

如果它仅用于存储8位类型的输入容器 - 例如, char或unsigned char。例如,如果使用32位类型(右移后的值大于15),则以下代码将失败 - 建议您始终使用掩码以确保查找索引始终在范围内。

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

如果传入包含unsigned longs的容器,预期的行为是什么 - 为了使它成为泛型类,它应该能够处理32位数字到hext字符串的转换。

这仅在输入是容器时才有效 - 如果我只想转换单个字节怎么办?这里的一个建议是将代码重构为一个核心函数,它可以转换单个字节(hex =&gt; ascii和ascii =&gt; hex),然后提供额外的函数来使用这个核心函数来转换字节容器等。

在asciihex()中,如果输入容器的大小不能被2整除,就会发生不好的事情。使用:

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

是危险的,因为如果容器大小不能被2整除,则增加2会使迭代器超过容器的末尾,并且与end()的比较将永远不会起作用。这在某种程度上受到了断言调用的保护,但断言可以编译出来(例如,它经常在发布版本中编译出来),所以将它作为if语句会更好。

答案 7 :(得分:1)

我发现的问题:

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进行掩码使其不区分大小写,并且可以在char [0x20]中执行结果查找而不会出现溢出风险。非十六进制字符只会给你奇怪的值。

答案 8 :(得分:0)

我认为玩具代码的原因是没有错误检查。

我可以传递它两个向量,它会很乐意尝试做一些事情,并制造一个完整的混乱,产生随机的胡言乱语。