如何使用sscanf分析字符串数据?

时间:2019-08-07 17:59:04

标签: c string format

仅当字符串与以下字符串结构匹配时,才如何将字符串拆分为两个字符串(数组名称,索引号):“ ArrayName [index]”。 数组名称最多可以包含31个字符,索引最多可以包含3个字符。

我找到了以下示例,该示例假定可与“ Matrix [index1] [index2]”一起使用。我真的不明白它是怎么做的,以便分解我需要得到琴弦的那部分。

sscanf(inputString, "%32[^[]%*[[]%3[^]]%*[^[]%*[[]%3[^]]", matrixName, index1,index2) == 3

这次尝试失败了,我想念什么?

sscanf(inputString, "%32[^[]%*[[]%3[^]]", arrayName, index) == 2

3 个答案:

答案 0 :(得分:1)

  

仅当字符串与以下字符串结构匹配时,才如何将字符串拆分为两个字符串(数组名称,索引号):

使用sscanf,您不需要。如果您的意思是说您输入的内容与模式不匹配,那么您就可以依靠任何东西而无需修改。这是因为sscanfscanf系列的其余部分一样,是线性处理其输入和格式的,没有回溯,并且根据设计,它会在成功匹配输入字段时填充它们。因此,如果您使用分配多个字段或带有尾随文字字符的格式进行扫描,那么即使发生匹配失败,也可能会为某些字段存储结果。

但是如果您认为可以,那么@gsamaras's answer提供了一种几乎正确的方法,可以使用sscanf根据您指定的格式来解析和验证字符串。该答案还很好地解释了格式字符串的含义。这样做的问题在于,它无法区分完全匹配格式的输入和输入的最后]匹配失败或后面没有其他字符的输入。

以下是该代码的一种变体,它也解决了这些后端问题:

  char array_name[32] = {0}, idx[4] = {0}, c = 0;
  int n;

  if (sscanf(str, "%31[^[][%3[^]]%c%n", array_name, idx, &c, &n) >= 3
          && c == ']' && str[n] == '\0')
    printf("arrayName = %s\nindex = %s\n", array_name, idx);
  else
    printf("Not in the expected format \"ArrayName[idx]\"\n");

格式上的差异是用]伪指令替换文字终止符%c,该伪指令匹配任何一个字符,并添加%n伪指令,导致字符数到目前为止已存储的读取输入中,没有消耗任何输入。

这样,如果返回值至少为3,那么我们就知道整个格式都已匹配(%n不会产生匹配失败,但是文档尚不清楚,并且行为是否不一致是否会导致匹配失败)返回的字段计数)。在那种情况下,我们检查变量c以确定是否有一个期望的]结束符,并且我们使用记录在n中的字符数来验证字符串被解析(因此str[n]指向字符串终止符)。

在这一点上,您可能想知道这一切到底有多复杂和神秘。您这样做是正确的。解析结构化输入是一个复杂而棘手的命题,一方面,scanf系列函数也很难使用。对于像您这样的情况,最好使用regex匹配器,或者最好使用机器生成的词法分析器(请参见lex),并可能通过机器生成的解析器进行扩展(请参见yacc) )。甚至手写解析器通过字符串功能和字符比较来处理输入字符串,也可能会有所改进。无论如何,它仍然很复杂,但是那些工具至少可以使它变得不那么神秘。

注意 :以上假设索引可以是最多三个字符的任何字符串。如果您要表示它必须是数字,也许是一个十进制数,或者也许是非负数,那么可以调整格式以达到此目的。

答案 1 :(得分:0)

一个让您入门的天真例子:

#include <stdio.h>
#include <string.h>

int main(void)
{
  char str[] = "myArray[123]";
  char array_name[32] = {0}, idx[4] = {0};

  if(sscanf(str, "%31[^[][%3[^]]]", array_name, idx) == 2)
    printf("arrayName = %s\nindex = %s\n", array_name, idx);
  else
    printf("Not in the expected format \"ArrayName[idx]\"\n");
  return 0;
}

输出:

arrayName = myArray
index = 123

可以找到容易的,非预期的格式,例如“ ArrayNameidx”和“ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP [idx]”,但找不到“ ArrayName [idx””。

sscanf()的本质是告诉它在哪里停止,否则%s会读到下一个空格。

此否定的扫描集%[^[]表示已读,直到找到一个开括号。

此否定的扫描集%[^]]表示已读,直到找到右括号为止。

注意:我分别使用31和3作为宽度说明符,因为我们要为NULL终止符保留最后一个插槽,因为假定数组的名称最多为31个字符,而索引3为最多。令牌的数组大小为允许的最大长度加一。

答案 2 :(得分:0)

  

如何使用sscanf分析字符串数据?

使用"%n"检测完成的扫描。


  

数组名称最多可以包含31个字符,索引最多可以包含3个字符。

为便于说明,我们假设索引需要限制为数值[0-999]。

使用字符串文字串联来更清楚地显示格式。

char name[32];  // array name can be 31 characters
#define NAME_FMT "%31[^[]"
char idx[4];   // 
#define IDX_FMT "%3[0-9]"

int n = 0;  // be sure to initialize
sscanf(str,  NAME_FMT "[" IDX_FMT "]" "%n", array_name, idx, &n);

// Did scan complete (is `n` non-zero) with no extra text?
if (n && str[n] == '\0') {
  printf("arrayName = %s\nindex = %d\n", array_name, atoi(idx));
} else {
  printf("Not in the expected format \"ArrayName[idx]\"\n");
}