【问题标题】:Missing punctuation from C++ hex2binC++ hex2bin 中缺少标点符号
【发布时间】:2013-09-25 04:59:06
【问题描述】:

在尝试在 GCC/Linux C++ 中复制 PHP 的 bin2hex($s)pack('H*',$s)(在 PHP 5.4.3+ 中也称为 hex2bin($s))时,我似乎已经弄清楚了,只是它出于某种奇怪的原因而删除了标点符号。你能弄清楚我在 hex2bin() 函数中可能做错了什么吗?我将 PHP 的 bin2hex() 与我的进行了比较,它似乎在那里正常工作,所以问题出在 hex2bin()。

#include <strings.h>
#include <string>
#include <stdio.h>
#include <stdlib.h>
using namespace std;

string bin2hex(string s) {
  int nLen = s.length();
  string sOut;
  char cBuff[2];
  for (int i = 0; i < nLen; i++) {
    sprintf(cBuff,"%.2x",s[i]);
    sOut.append(cBuff);
    cBuff[0] = '\0';
  }
  return sOut;
}

string hex2bin(string s) {
  int nLen = s.length();
  string sOut;
  char cBuff1[2];
  char cBuff2[2];
  char cBuff[1];
  int n,n1,n2;
  for (int i = 0; i <= nLen; i+=2) {
    sprintf(cBuff1,"%c",s[i]);
    sprintf(cBuff2,"%c",s[i+1]);
    n1 = atoi(cBuff1);
    n2 = atoi(cBuff2);
    n = (n1 * 16) + n2;
    sprintf(cBuff,"%c",n);
    sOut.append(cBuff);
    cBuff[0] = '\0';
    cBuff1[0] = '\0';
    cBuff2[0] = '\0';
  }
  return sOut;
}

int main() {
  string s;
  string sResult;  
  s = "This is a 123 test.";
  sResult = bin2hex(s);
  printf("ENCODED: %s\n",sResult.c_str());
  sResult = hex2bin(sResult);
  printf("UNENCODED: %s\n",sResult.c_str());
  return 1;
}

这会发出:

ENCODED: 5468697320697320612031323320746573742e
UNENCODED: This is a 123 test

【问题讨论】:

  • 那是……相当令人不安的代码。另外:十六进制是 encoding not encryption。我希望你知道其中的区别。 (!?!?)。 (我冒昧地将其重写为 C++ 实现。请参阅我的答案)
  • 你期待什么 n2 = atoi(cBuff2);当输入结束时到达“e”时该怎么办?它将返回 0,n 将是 2*16+0 = 32,这是一个空格。我认为您不能使用 atoi - 您需要可以接受基数 af 16 的东西。 strtol 可能会起作用。可能还有 sscanf。
  • @sehe 在 Twitter 上查找我,让我们通过电子邮件联系。在 20 年没有接触 C++ 之后,我正在重新审视它。您的反馈是有保证的,任何有关我如何在该平台上更好地编码的详细反馈都将受到赞赏。例如,我什至没有关注内存泄漏,因为我不知道如何正确地释放东西。
  • @sehe 顺便说一句,这篇文章被用作我正在研究的一些跨平台加密的一部分,我忘记更改函数名称以反映你是正确的,它是编码。

标签: c++ string hex binary-data printf


【解决方案1】:

好了,卷起袖子:我们来看看C++版本:

Live on Coliru

  • 除非需要,否则不要使用 C 字符串(sprintf 构建两个字符的字符串不是...非常有效)
  • 使用 iostreams 编码/解码十六进制数字 (std::hex)
  • hex2bin 可以优化,但我选择“更简单”
  • 我在hex2bin 上添加了少量的输入清理
#include <string>
#include <sstream>
#include <iomanip>

std::string bin2hex(std::string const &s) {
    std::ostringstream oss;

    for (unsigned char ch : s)
        oss << std::hex << std::setw(2) << std::setfill('0') << (int) ch;

    return oss.str();
}

#include <cassert>
std::string hex2bin(std::string const& s) {
    assert(s.length() % 2 == 0);

    std::string sOut;
    sOut.reserve(s.length()/2);

    std::string extract;
    for (std::string::const_iterator pos = s.begin(); pos<s.end(); pos += 2)
    {
        extract.assign(pos, pos+2);
        sOut.push_back(std::stoi(extract, nullptr, 16));
    }
    return sOut;
}

#include <iostream>
int main() {
    std::cout << "ENCODED: " << bin2hex("This is a 123 test.")          << "\n";
    std::cout << "DECODED: " << hex2bin(bin2hex("This is a 123 test.")) << "\n";
}

输出:

ENCODED: 5468697320697320612031323320746573742e
DECODED: This is a 123 test.

【讨论】:

  • 进一步改进,在hex2bin中使用std::stoi而不是istringstream
  • 我的 G++ 在 bin2hex 中的 for auto 行上给我一个错误。它说:error: expected initializer before ‘:’ token
  • 我将 bin2hex 中的 for 切换为 for (int i=0;i&lt;nLen;i++),然后将 bin2hex 中的 (int) ch 切换为 (int) s[i],并在您的帮助下完成了编码工作。我的编译器太旧了。
  • 我希望我知道编译所需的内容。在找到它之前,我必须进行试验/错误。另外,我需要了解更多关于&lt;&lt;&gt;&gt; 运算符以及ostringstreamistringstreamhexsetw(2) 之类的知识。
  • @hanshenrik 很棒的地方。已修复(在过去的某个地方,我已经开始使用unsigned char 处理将字符作为序数处理的任何内容(包括来自&lt;cctype&gt; 的所有内容),但显然不是在我写这个答案的时候)。从技术上讲,该行为取决于实现,但如果您的实现默认具有 signed char,这将无济于事。
【解决方案2】:

除了句点 '.' 之外,您很幸运:十六进制数字没有使用实际的十六进制值。但是,在您获得2e 期间,您尝试使用atoi("e") 解码e,大致如下:这将不起作用,因为atoi() 需要十进制值。您可以改用strtol(str, 0, 16) 来解码十六进制值。

请注意,当您使用sprintf() 时会出现一些缓冲区溢出:此函数会写入一个终止空字符。一般来说,您最好使用snprintf() 以避免缓冲区溢出。此外,在您的解码例程中,您访问超出字符串末尾的值(您使用i &lt;= nLennLen = s.length(),然后访问s[i]s[i+1])。当然,代码太复杂了:

#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>

std::string bin2hex(std::string s) {
    std::ostringstream out;
    out << std::hex << std::setfill('0');
    for (char c: s) {
        out << std::setw(2) << int(c);
    }
    return out.str();
}

std::string hex2bin(std::string s) {
    std::string rc;
    int nLen = s.length();
    int tmp;
    for (int i(0); i + 1 < nLen; i += 2) {
        if (std::istringstream(s.substr(i, 2)) >> std::hex >> tmp) {
            rc.push_back(tmp);
        }
    }
    return rc;
}

int main() {
  std::string s;
  std::string sResult;  
  s = "This is a 123 test.";
  sResult = bin2hex(s);
  std::cout << "ENCRYPTED: " << sResult << '\n';
  sResult = hex2bin(sResult);
  std::cout << "UNENCRYPTED: " << sResult << '\n';
  return 1;
}

【讨论】:

  • 哦,嘿,+1,另一个忠实的 iostreams 产品。我有点失望你留下了误称 ENCRYPTEDUNENCRYPTED (sic) :/
  • ERROR: bin2hex() 将为 128 以上的任何字符返回错误值...要修复它,请将 for (char c: s) { 更改为 for (unsigned char c: s) { -(我已经验证了使用无符号修复,它为 0x00 - 0xFF 之间的每个字符返回正确的值)
【解决方案3】:

您的代码无法正确转换十六进制数字,因为 atoi 只能处理十进制数字。试试这个

sprintf(cBuff1,"%c",s[i]);
sprintf(cBuff2,"%c",s[i+1]);
n1 = strtoul(cBuff1, 0, 16);
n2 = strtoul(cBuff2, 0, 16);

你的for循环也应该是

for (int i = 0; i < nLen; i+=2) {

【讨论】:

    【解决方案4】:
    n1 = atoi(cBuff1);
    n2 = atoi(cBuff2);
    n = (n1 * 16) + n2;
    

    如果cbuff1"a",那么这是行不通的,因为a 不是数字。它适用于“0-9”的数字,但不是“a-f”。

    您需要将非数字转换为数值。

    有很多方法可以将十六进制值字符串转换为字节。我认为这是相当不错的:

    int hexchar(char c)
    {
       if (c >= '0' && c <= '9') return c - '0';
       // if you need to support upper-case hex:
       // c = tolower(c); 
       if (c >= 'a' && c <= 'f') return c - 'a' + 10; 
       // If we get here, panic
       cout << "Error, invalid hex digit:" << c << endl;
       return -1;
    }
    
    int hexbyte(string s)
    {
        for(i = 0; i < s.length(); i+=2)
        {
           char c = hexbyte(s[i]);
           c <<= 4;
           c += hexbyte(s[i+1];
           cout << c;
        }
    }
    

    【讨论】:

      【解决方案5】:

      试试这些琐碎的例程,对 C 和 C++ 有好处

      /*------------------------------------------+
      |       bin2hex     bin2hex     bin2hex     |
      +------------------------------------------*/
      static  char *bin2hex(unsigned char *s, long L)
      {
          static  char hex[2048];
          long i,l=0;
          for (i=0; i<L; i++) l+=sprintf(&hex[l], "%02x", 0xFF & (*(s+i)));
          hex[l]=0;
          return hex;
      }
      /*------------------------------------------+
      |       hex2bin     hex2bin     hex2bin     |
      +------------------------------------------*/
      static  char *hex2bin( char *s)
      {
          static  char bin[2048];
          unsigned int i,e,l=0,L=strlen(s);
          for (i=0; i<L; i+=2) { sscanf(s+i, "%02x",&e); bin[l++]=(char)e; }
          bin[l]=0;
          return bin;
      }
      

      【讨论】:

      • 也许微不足道,但线程不安全,并且对于超过 2048 的输入可能会崩溃 =/
      猜你喜欢
      • 2023-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-17
      • 2010-12-17
      • 1970-01-01
      • 1970-01-01
      • 2012-05-08
      相关资源
      最近更新 更多