【问题标题】:Converting binary data to printable hex将二进制数据转换为可打印的十六进制
【发布时间】:2009-01-05 12:22:27
【问题描述】:

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 “1234abcd”) 4:断言在调试和发布模式下都会捕获错误 5:当这些函数被调用时,字符串的大小已经被检查过了,如果出现严重错误,断言用于终止处理 6:需要一些评论

任何其他想法表示赞赏。

【问题讨论】:

  • Patrick 你的问题没什么可反驳的,但是标题很模糊,不幸的是我想不出更准确和有意义的东西,但如果有人有一个好主意,那就太好了,毕竟问题可能意味着任何事情。
  • asciihex的第一行不应该是:size_t size = in.size();
  • 罗伯特这个问题我也有同样的想法,但也想不出什么简洁有意义的东西。
  • 也许 HexToBinary 和 BinaryToHex 是更有意义的名称,这里的术语 ASCII 具有误导性,特别是因为模板的使用表明可能是 unicode。否则十六进制为 ASCII。
  • 请尝试为您的问题指定一个标题,现在就是您的答案。没有人能够准确地搜索到这个

标签: c++ templates stl


【解决方案1】:

鉴于您在标准 C scanfprintf 函数中有直接的十六进制转换,似乎很多模板化代码实现的很少。何必呢?

【讨论】:

  • 我也是这么想的。
  • sscanf/printf 不是类型安全的。流 > 运算符是。
  • 是的,但是你可以用非常简单的函数包装 sscanf 和 sprintf 来实现类型安全。上面的模板代码对于这么简单的事情来说太过分了,模板的使用错误地表明代码是类型友好的。
  • 我认为这个答案来自对函数用途的误解(问题出在问题中,而不是您对它的阅读 - 我在问题中添加了一些内容)。我看不到任何使用 printf 将字节数组更改为可打印 ascii 并返回的“好”方式。
  • 实际上我根本看不到如何使用 sprintf 将 0x30 变成 0x00。你同意还是我傻?
【解决方案2】:

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

特别是:

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

我需要花点时间才能理解这一点,如果我继承了代码,我会很恼火。

【讨论】:

  • 同样,肯定会令人困惑。
  • “一会儿”?无论是天才还是轻描淡写。
  • 该行肯定需要注释,但一旦选择了这种转换方法,我无论如何都看不到简化它。将其拆分为多行...
  • 同意,目前尚不清楚如何改进它。可能将一些检查移动到子方法中。尽管如此,它还是让我眼睛发麻。
  • 我肯定会用一个内联函数 'getHexValue' 替换 "(*it > '9' ? *it - 0x07 : *it) - 0x30)",然后调用两次。我也不确定掩码的值:如果输入字符串是垃圾,谁在乎输出什么值?如果输入字符串有效,则掩码无效。
【解决方案3】:

它应该做什么? hexascii 或 asciihex 没有众所周知的公认含义,因此名称应该更改。

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

【讨论】:

  • 好电话,我现在才注意到。乍一看,我认为它是一个用于在各种类型的整数和十六进制之间转换的函数。重新阅读它似乎是为了在二进制块(以字节为单位)和十六进制之间进行转换。
  • 编辑问题,将二进制数组更改为可打印格式,然后再返回。
【解决方案4】:

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

resize 初始化不必要的输出元素(使用 reserve 代替)。

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

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

【讨论】:

  • 如果您使用保留,代码将不起作用。如果在调用reserve 之前out.size() 为0,那么在调用reserve 之后它仍然为0,因此循环不会执行。见gotw.ca/gotw/074.htm
  • 确实,您需要进行额外的调整。我的观点是对象的构造是不必要的。
【解决方案5】:

怎么了

*outit = hexDigits[*it]

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

【讨论】:

  • 如何将 0x30 链接到 0x00?我想你可以使用一张地图,但看起来有点矫枉过正。
  • 地图是一个简单的数组查找。只会有几个字节(在疯狂的最坏情况下为 256;128 更现实)。它会立即使用加法和乘法执行,仅此而已。
【解决方案6】:
  • 代码有断言语句,而不是正确处理错误条件(如果关闭断言,代码可能会崩溃)

  • for 循环具有危险的迭代器双倍增加 (it+=2)。特别是在您的断言没有触发的情况下。当您的迭代器已经结束并且您 ++ 它时会发生什么?

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

  • *outit= 行太复杂了。

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

【讨论】:

  • 谢谢Thorsten,我希望你能看到这个。在货物崇拜点上,代码允许我处理来自不同库的 2 个容器,而无需编写 2 个仅在参数上有所不同的函数,这是模板的目的之一。我没有考虑所有其他容器:(。
  • 我很重视您关于库之间可互操作的想法。但是你为什么要摆弄字符常量和晦涩的 ASCII 值呢?它的代码“介于排水沟和星星之间”。它试图优雅,但未能兑现承诺。您可以在这里轻松使用 C++ 字符串。
【解决方案7】:

我看到的一些问题:

如果它仅用于存储 8 位类型的输入容器,这将非常有用 - 例如字符或无符号字符。例如,如果与右移后的值大于 15 的 32 位类型一起使用以下代码将失败 - 建议您始终使用掩码以确保查找索引始终在范围内。

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

如果你传入一个包含无符号长整数的容器,预期的行为是什么 - 作为一个泛型类,它可能也应该能够处理 32 位数字到十六进制字符串的转换。

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

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

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

是危险的,因为如果容器大小不能被 2 整除,那么增量为 2 将使迭代器前进到容器的末尾,并且与 end() 的比较永远不会起作用。这在一定程度上可以通过 assert 调用得到保护,但 assert 可以被编译出来(例如,它通常在发布版本中编译出来),因此最好将其设为 if 语句。

【讨论】:

  • asciihex() 第二行的断言检查输入大小是否可被 2 整除,因此 +=2 在这种情况下是安全的 - 我同意它至少看起来很危险,而且我我想我自己的代码会有所不同。
  • @jrbushell,断言只会包含在调试版本中。它对发布代码没有影响,并且有助于测试而不是运行时检查
  • assert 不仅在我们的构建环境中签入调试代码(尽管您可以对其进行配置)
【解决方案8】:

我发现的问题:

hexascii 不检查sizeof(T2::value_type)==1

hexascii 取消引用 it 两次,asciihex 更多。没有理由这样做,因为您可以存储结果。这意味着您不能使用 istream_iterator。

asciihex 需要一个随机迭代器作为输入,因为使用了 (it+1) 和 (it+=2)。如果您只使用 (++it),该算法可以在前向迭代器上工作。

(*it &gt; '9' ? *it - 0x07 : *it) - 0x30 可以简化为*it - (*it &gt; '9' ? 0x37 : 0x30),所以只剩下一个无条件减法。不过,数组查找会更有效。减去 0x30。 “0”将变为 0;“A”将变为 0x11,“a”将变为 0x31。使用 0x1f 进行掩码以使其不区分大小写,并且您可以在 char[0x20] 中进行结果查找而没有溢出风险。非十六进制字符只会给你奇怪的值。

【讨论】:

    【解决方案9】:

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

    我可以将两个向量传递给它,它会很高兴地尝试做某事并弄得一团糟,产生随机的乱码。

    【讨论】:

      猜你喜欢
      • 2012-06-26
      • 1970-01-01
      • 2014-03-07
      • 1970-01-01
      • 2022-11-29
      • 2016-08-28
      • 2013-07-25
      相关资源
      最近更新 更多