sscanf() 如何在 C 中拆分字符串

How sscanf() splits a string in C

所以基本上我有一个简单的字符串,我正在尝试使用 sscanf() 拆分这个字符串并将值存储在适当的变量中:

#include<stdio.h>
int main()
{

    int i=10,j;
    char ch = 'A',ch2;
    float a=3.14,b;
    char str[20]="10A3.140000";


    sscanf(str,"%d%c%f",&j,&ch2,&b);
    printf("%d %c %f\n",j,ch2,b);
    return 0;
}

现在我的疑问是 sscanf 究竟是如何知道在哪里拆分字符串的。这里的值 10 进入变量 j。字符 A 进入变量 ch23.140000 进入变量 b。 sscanf 究竟是如何解析这个字符串并将值存储在不同的变量中的。如果您能解释 sscanf 究竟如何与任何字符串一起工作,我们将不胜感激。我很难理解它。

scanf 根据您在格式字符串中定义的格式说明符使用 输入字符串。

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

sscanf(str,"%d%c%f",&j,&ch2,&b);
  • %d 解析搜索 十进制整数 的字符串。这意味着只接受 0-9 范围内的数字字符。此外,接受前导符号字符(如数字 -123)。所有满足此条件的字符都被消耗
  • %c 扫描 any 字符,将其 ASCII 值保存在目标变量中。消耗单个字符
  • %f 解析搜索 floats 的字符串。与 %d 情况一样,所有十进制数字和符号字符都被接受,但它也接受 . 字符,将其解释为浮点数

所以,让我们再看看你输入的字符串:

char str[20]="10A3.140000";
  1. scanf 从第一个字符开始解析。由于存在小数,因此它会消耗它们,直到找到第一个 non-numeric 字符 ('A')。整数 10 存储在 j.

    如果字符串在这里完成,scanf 将 returned 1.

  2. scanf 从字母 'A' 开始继续解析。因为我们有 %c 格式说明符,这个字符被消耗并且值 65('A' 的 ASCII)存储在 ch2.

    如果字符串在这里完成,scanf 将 returned 2.

  3. 字母'A'被消耗后scanf从字符'3'继续解析。 %f 格式说明符将接受所有剩余字符(因为它们都是有效的),值 3.14 存储在变量 b.

    scanf 现在 returns 3.


我试图理解 return 由 scanf 编辑的值,因为它正在检查您是否可以控制正在发生的事情。你的情况

if( sscanf(str,"%d%c%f",&j,&ch2,&b) == 3 )
{
    printf("%d %c %f\n",j,ch2,b);
}

将防止格式错误的字符串导致未定义行为的情况。要理解这种情况,请考虑以下字符串:

char str[20]="10AB3.140000";

我们在第一个整数后有两个 非数字 字符。那么:

  • %d 仍然会消耗值 10
  • %c 仍然会消耗字母 'A'
  • %f 不会使用下面的字符串,因为子字符串 "B3.140000" 不能被解析为浮点数 :前导 'B' 强制 scanf 中断解析,值 2 将被 returned.

因此,如果不检查 return 值,未初始化 变量 b 将被访问。

这个解释很少 over-simplified,sscanf 中的内容不止于此,因为源代码本身就有 3,000 多行代码。

下面列出了您在示例中提到的格式说明符及其对应的匹配模式
%d : [0-9],[-,+]
%f : [0-9],[-,+,.]
%c : [a-z],[A-Z], 其他 ASCII 字符

正在解析

  1. 首先,对于%d,它会先查找[+,-]个符号,如果找不到则依次查找[0-9]个个位数。一旦它在字符串中看到 non-single 数字 (A = 65),它将结束对 %d 的搜索并将 int 的值保存到传递给 sscanf.
  2. 其次,对于 %c,现在它将采用下一个传递的格式说明符,即 %c,并从之前停止的位置重新开始。这次它只需要从字符串中读取一个字节并将其保存到传递给 sscanf
  3. 的相应地址
  4. 第三,对于%f,它会先搜索[+,-]符号,如果没有找到再寻找.和个位数[0-9]。这一直持续到它得到一些不属于 %f 的东西,在这种情况下将是 [=34=]。并保存到传给sscanf
  5. 的对应地址

最后,一旦一切都完成并且字符串被拆分,它 returns 解析的参数总数。因此,将传递的参数列表的数量与返回值进行比较是一个很好的编程习惯。

参考:

  1. sscanf source code
  2. vfscanf source code