为什么 sscanf 忽略指定的宽度

Why does sscanf ignore the specified width?

本文关键字:sscanf 为什么      更新时间:2023-10-16

我正在使用此代码将固定大小为 32 的十六进制字符串转换为 16 字节的 uint8 数组。

const uint8_t* c = "0123456789abcdef0123456789abcdef";
uint8_t Bytes[16];
for (int i = 0; i < 16; i++) {
    sscanf (&c[2*i], "%2hhx", &(Bytes[i]));
}

尽管hh指定目标宽度为 1 字节,但每一步写入 4 个字节。因此,代码在缓冲区末尾写入 3 个字节。为什么?

(现在,我使用一个临时 int 修复了它,该 int 在每个步骤中都复制到数组中。

要重现:

#include <stdint.h>
#include <string>
void main (int argc, char* argv[])
{
    const char* c = "0123456789abcdef0123456789abcdef";
    uint8_t b[20];
    for (int i = 0; i < 20; i++) {
        b[i] = i;
    }
    for (int i = 0; i < 16; i++) {
        sscanf (&c[2*i], "%2hhx", &(b[i]));
    }
    for (int i = 0; i < 20; i++) {
        fprintf(stdout,  "%02xn", (int)(b[i]));
    }
}

预期输出为0123456789血型光盘英 孚0123456789血型光盘英 孚10111213

但是,使用 Visual Studio 2010 的实际输出是:

0123...光盘英 孚00000013

一开始你的代码有一个小问题。

const uint8_t* c = "0123456789abcdef0123456789abcdef";

在C++中,没有指定charsigned还是unsigned。更准确地说,类型 charsigned charunsigned char是不同的,事实上你必须考虑到这一点重载函数和专用模板时。

现在的代码是:

const char* c = "0123456789abcdef0123456789abcdef";
uint8_t Bytes[16];
for (int i = 0; i < 16; i++) {
    sscanf (&c[2*i], "%2hhx", &(Bytes[i]));
}

让我们分析一下您的格式字符串:

2  : maximum field witdh to be read
hh : expecting a pointer to signed or unsigned char
x  : means unsigned hex-input
(http://linux.die.net/man/3/scanf)

有了这些信息,到目前为止它看起来是正确的。

据我所知,符合 C99 标准的库* 没有问题。


*:在这方面,MSVC 库不支持hh说明符。

非常适合我:

#include <stdio.h>
#include <stdint.h>
int main()
{
    const char* c = "0123456789abcdef0123456789abcdef";
    unsigned char Bytes[16];
    int i;
    for (i = 0; i < 16; i++)
    {   
            sscanf (&c[2*i], "%2hhx", &(Bytes[i]));
    }   
    for (i=0; i < 16; ++i)
    {   
        fprintf(stdout,  "%02xn", (int)(Bytes[i]));
    }   
}

用途:

> gcc gh.c 
> ./a.out
01
23
45
67
89
ab
cd
ef
01
23
45
67
89
ab
cd
ef

我在Mac OS X上使用gcc 4.2.1构建并运行了以下代码:

#include <stdio.h>
#include <stdint.h>
int main(void)
{
    const char *c = "0123456789abcdef0123456789abcdef";
    uint8_t b[20] = { 0 };
    int i;
    for (i = 0; i < 20; i++) {
        b[i] = i;
    }
    for (i = 0; i < 16; i++) {
        sscanf (&c[2*i], "%2hhx", &b[i]);
    }
    for (i = 0; i < 20; i++) {
        printf("%02x", b[i]);
    }
    printf("n");
    return 0;
}

它的行为似乎符合预期:

$ gcc -v
...
gcc version 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00)
$ gcc -Wall scanf_hex.c
$ ./a.out 
0123456789abcdef0123456789abcdef10111213
$ 

请尝试使用编译器等构建和运行上述代码,以便我们查看它是否是可能的编译器/库错误,或者它是否是您代码中的其他问题。(你可能想要同时尝试调试版本和发布版本。

iostream 的 c++ 解决方案可能如下:

  #include<iostream>
  #include<sstream>
  #include<iomanip>      
  using namespace std;
  //...
  const char* c = "0123456789abcdef0123456789abcdef";
  unsigned char Bytes[16];
  stringstream s_in(c);
  for (int i = 0; i < 16; i++) 
  {
      string s;
      s_in >> setw(2) >> s;
      unsigned int t;
      stringstream(s) >> hex >> t;
      Bytes[i] = t;
  }

请注意,如果变量类型不是字符串,s_in 不想服从 setw(2)。此外,如果变量是 char 类型,则宽度一定为 1,因此从 int 转换。

要对其进行测试,请执行以下操作:

  for (int i = 0; i < 16; i++) 
  {
    cout << setfill('0') << setw(2) << hex << (int) Bytes[i] << " ";
  }