【问题标题】:Reading multiple values from formatted string using sscanf使用 sscanf 从格式化字符串中读取多个值
【发布时间】:2017-08-18 11:11:34
【问题描述】:

我正在尝试从字符串中提取两个值。第一个是 8 位十六进制值,第二个是无符号 1-4 位值。这些值之前还应该有一个命令,该命令告诉程序如何处理这些值,在本例中为“读取”。格式的一些示例:

"read 0x1234ABCD 2000"
"read 0x00000001 10"

我想同时提取两个值并确认格式,并且有以下代码行:

uint addr;
uint len;

int n = sscanf(str, "read 0x%x[0-9a-fA-F]{8} %u[0-9]{1,4}", &addr, &len);

if (n != 2){
    // Wrong format...
}

十六进制值读取正确,但第二个值未正确读取,n 始终为 1。我做错了什么?

【问题讨论】:

  • scanf not 做正则表达式。要读取最多八个十六进制数字,只需使用"%8x"。参见例如this scanf (and family) reference.
  • @Someprogrammerdude。嗯,我打算回答这个问题(stackoverflow.com/questions/16013031/…)。那不是正则表达式吗?
  • 假设你的意思是例如"%[0-9]",那么不,它不是正则表达式,而是使用 simple 字符类的变体。
  • 好的,我在 sscanf "read 0x%8x %u" 中尝试了以下操作,效果很好。谢谢!
  • @AndrewHenle 同意scanf() 可能是一个挑战,但更大的挑战,IMO,是语法要求从一开始就不完整:只提供 一些 可接受的形式和一些不可接受的形式。当原始目标不精确时,任何解决方案都容易出现维护问题。

标签: c regex scanf


【解决方案1】:

我做错了什么?

Input:  "read 0x1234ABCD 2000"
Format: "read 0x%x[0-9a-fA-F]{8} %u[0-9]{1,4}"

输入 "read 0x" 匹配格式 "read 0x"。到目前为止还不错。

输入 "1234ABCD" 匹配格式 "%x"。目前很好。 +1 返回值。

输入 " " 与格式 "[" 不匹配。扫描停止。 sscanf() 返回 1。


替代方案,将第二个值读取为十进制值。

const char *f1 = "read 0x%x %u";
const char *f2 = "read 0x%x%u";   // Space not need, yet looks good
const char *f3 = "read %x%u";     // Always read addr as hex, even with/without 0x
const char *f4 = "read %x %u";
const char *f5 = "read%x%u";

unsigned addr;
unsigned len;
int n = sscanf(str, fn, &addr, &len);  // select format from above

上面的代码没有失败

"read 0x0x123 +1234"
"read 0x123 456 xyz"
"read 0x123 12345"
"read 0x+123 -123"

OP 是否需要更多的错误检查。 8 将 addr 的文本输入限制为 8 个非空白字符。 sentinel 检测拖尾非空白垃圾。

unsigned addr;
unsigned len;
char sentinel;
int n = sscanf(str, "read 0x%8x %4u %c", &addr, &len, &sentinel);
if (n != 2){
  // Wrong format...
}

上面确实失败了

"read 0x123 456 xyz"

最接近 OP 原始代码的内容需要更多的工作。使用"%[...]" 测试允许的扫描集。

#define F_RD    "read"
#define F_SP    "%*[ ]"
#define F_ADDR  "0x%8[0-9a-fA-F]"
#define F_LEN   "%4[0-9]"
#define F_SEN   " %c"
char addr_s[8+1];
char len_s[4+1];
char sentinel;
int n = sscanf(str, F_RD F_SP F_ADDR F_SP F_LEN F_SEN, addr_s, len_s, &sentinel);
if (n == 2){
  // Success
  unsigned long addr = strtoul(addr_s, (char **)NULL, 16);
  unsigned len = strtoul(len_s, (char **)NULL, 10);
  ...
}

除了我允许x 或X 之外,我没有看到任何输入行表明此代码不会像OP 所希望的那样失败/通过。

【讨论】:

  • 请注意,错误检查版本不会因输入"read 0x75088FF08"而失败。
  • @DavidBowling foiled again 查看编辑
【解决方案2】:

要同时解析十六进制和十进制编码数字,请使用%i 转换说明符。

无法使用您在格式字符串中使用的正则表达式语法指定位数,这解释了第二次转换失败的原因。

这是一个更简单的版本:

 int n = sscanf(str, "read %i %i", &addr, &len);

【讨论】:

  • 第一个 %i 真的能保证解析成十六进制吗?我知道号码是否有 A-F 字符,但如果号码是“00000010”怎么办?这会读作 16 还是 10?
  • @Martin-- 您的问题的答案在文档中。 %i 需要integer constant 格式的输入。 00000010 将被解释为八进制数,十进制为 8。如果要将输入解释为十六进制,请使用 0x00000010 或 0X00000010。
猜你喜欢
  • 2017-10-11
  • 1970-01-01
  • 2023-03-10
  • 2021-06-30
  • 2016-08-14
  • 1970-01-01
  • 2011-02-20
  • 2012-06-06
相关资源
最近更新 更多