将大整数扫描(sscanf)为字节

时间:2018-10-17 15:09:53

标签: c scanf

我需要sscanf一个20位数字,该数字刚刚超出uint64_t的范围。如果无法在uint64_t中包含完整的数字,我希望将其作为uint8_t[10](每个字节代表2位数字),可以这样做:

const char *resp = "+QCCID: 89445003071864431280";

void parse_bytes() {
  uint8_t bytes[10] = {0};
  sscanf(resp, "+QCCID: %2hhu%2hhu%2hhu%2hhu%2hhu%2hhu%2hhu%2hhu%2hhu%2hhu", 
         &bytes[9], &bytes[8], &bytes[7], &bytes[6], &bytes[5], 
         &bytes[4], &bytes[3], &bytes[2], &bytes[1], &bytes[0]);

  // print just for testing, not the end result
  for(int i=9; i>=0; i--) {
    printf("%02d", bytes[i]);
  }
}

这很好用,但不是特别漂亮的代码。有没有更简洁的方法可以将类似的内容扫描到数组中?

要弄清楚结果的意图:该结果将不会用于计算,它将通过Bluetooth Low Energy发送到另一台设备。

更新:我最终得到了@PetarVelev建议的修改版本。由于这个问题范围之外的原因,我使用了位域结构。如果有人感兴趣,我将解决方案作为下面的答案之一。

3 个答案:

答案 0 :(得分:3)

代码可以使用循环和"%n"来跟踪扫描进度。

可能不漂亮,但确实允许更轻松地进行M更改。

#include <inttypes.h>
#include <stdio.h>
#define M 10

int parse_bytes(const char *resp) {
  uint8_t bytes[M] = {0};
  int n = 0;
  sscanf(resp, "+QCCID: %n", &n);
  if (n == 0) return -1;  // missing prefix 
  int m;
  for (m = 0; m < M; m++) { 
    resp += n;
    n = 0;
    sscanf(resp, "%2" SCNu8 "%n", &bytes[M - 1 - m], &n);
    if (n == 0) return -1;  // missing number
  } 
  if (resp[n]) return -1;  // trailing junk

  for (int i = m-1; i >= 0; i--) {
    printf("%02d", bytes[i]);
  }
  return m;
}

如果代码要确保连续的数字并且没有空格或符号字符,可以添加测试:

    n = 0;
    if (!isdigit((unsigned char) *resp)) return -1;  // digit expected.
    sscanf(resp, "%2" SCNu8 "%n", &bytes[M - 1 - m], &n);

未处理的案件。 OP断言“ 20位数字”。如果少于这个数,我们应该重新考虑整个方法。也许逐位扫描到d[M*2]中,然后从“右边”形成bytes[],或者:

  int n1, n2 = 0; 
  sscanf(resp, "+QCCID: %n%*[0-9]%n", &n1, &n2);
  if (n2 == 0 || n2 - n1 > 2*M) fail();

  // now process resp[n2-1] to resp[n1] with TBD code

很多可能性。

答案 1 :(得分:1)

根据问题和OP注释判断,您需要较小的占用空间,并且不会使用数字进行打印以外的任何操作。

然后,为什么不使用相同的技术来获取uint64_t中的前18位数字和uint8_t中的后两位数字。

int main()
{
    printf("Hello World");
    char arr[21] = "01234567890123456789";
    unsigned long long int a;
    unsigned char b;

    sscanf(arr, "%18llu%2hhu",&a,&b);

    printf("%18llu%2hhu",a,b);

    return 0;
 }

请注意,不会打印前导零。

这将减少1个字节的内存占用空间,显然,您不能减少这部分,因为它不能容纳8个字节(uint64_t)。

答案 2 :(得分:0)

我的最终解决方案如下。我最终得到了一个位字段结构,因为事实证明我需要在其他地方使用前6位数字。此外,它正在具有newlib nano的嵌入式系统上运行,该系统不支持64位格式,因此我无法扫描/打印uint64_t

当然,由于您不能接受位字段的地址,因此我首先扫描到uint32_t,然后创建该结构。

typedef struct __attribute__((packed)) {
  uint32_t d1_6   : 20;
  uint32_t d7_12  : 20;
  uint32_t d13_20 : 32;
} lte_sim_iccid_t;

lte_sim_iccid_t parse_into_struct() {
  uint8_t bytes[10] = {0};

  uint32_t i1 = 0, i2 = 0, i3 = 0;
  sscanf(resp, "+QCCID: %6u%6u%8u", &i1, &i2, &i3);

  lte_sim_iccid_t s = { i1, i2, i3 };

  // sanity check
  printf("ICCID: %u %u %u (size %lu bytes)\n", s.d1_6, s.d7_12, s.d13_20, sizeof(s));
  return s;
}