正确使用sscanf

Nah*_*hum 4 c input token

我应该得到一个输入行,可以是以下任何格式:

  • 在单词1和单词2之间必须有空格.
  • 在单词2和单词3之间必须有逗号.
  • 在单词2和单词3之间不是必须的空格 - 但是任何数量的空格都是可能的.

如何分离1,2和3个单词的情况并将数据放入正确的变量?

word1
word1 word2 
word1 word2 , word3
word1 word2,word3
Run Code Online (Sandbox Code Playgroud)

我想到了类似的东西:

sscanf("string", "%s %s,%s", word1, word2, word3);
Run Code Online (Sandbox Code Playgroud)

但它似乎没有用.

我使用严格的C89.

Jon*_*ler 23

int n = sscanf("string", "%s %[^, ]%*[, ]%s", word1, word2, word3);
Run Code Online (Sandbox Code Playgroud)

返回值n告诉您已成功完成了多少个分配.这%[^, ]是一个否定的字符类匹配,它找到一个不包括逗号或空格的单词(如果你愿意,可以添加标签).该%*[, ]匹配的是找到一个逗号或空间,但抑制的分配.

我不确定我是否会在实践中使用它,但它应该可行.但是,它未经测试.


也许更严格的规范是:

int n = sscanf("string", "%s %[^, ]%*[,]%s", word1, word2, word3);
Run Code Online (Sandbox Code Playgroud)

不同之处在于非赋值字符类只接受逗号. sscanf()在任何空格(或EOS,字符串末尾)之后停止word2,并在分配之前跳过空格word3.上一版允许在第二个和第三个单词之间留一个空格来代替逗号,这个问题并不严格允许.

正如pmg在注释中所建议的那样,分配转换规范应该给出一个长度以防止缓冲区溢出.请注意,长度不包括空终止符,因此格式字符串中的值必须小于数组的大小(以字节为单位).另请注意,while printf()允许您动态指定大小*,sscanf()*用于抑制赋值.这意味着您必须专门为手头的任务创建字符串:

char word1[20], word2[32], word3[64];
int n = sscanf("string", "%19s %31[^, ]%*[,]%63s", word1, word2, word3);
Run Code Online (Sandbox Code Playgroud)

(Kernighan&Pike建议在他们的(优秀的)"编程实践"或亚马逊编程实践 1999中动态格式化格式字符串.)


刚发现一个问题:给定"word1 word2 ,word3",它不读word3.有治疗方法吗?

是的,有一种治疗方法,实际上也是微不足道的.在非赋值的逗号匹配转换规范之前,在格式字符串中添加一个空格.从而:

#include <stdio.h>

static void tester(const char *data)
{
    char word1[20], word2[32], word3[64];
    int n = sscanf(data, "%19s %31[^, ] %*[,]%63s", word1, word2, word3);
    printf("Test data: <<%s>>\n", data);
    printf("n = %d; w1 = <<%s>>, w2 = <<%s>>, w3 = <<%s>>\n", n, word1, word2, word3);
}

int main(void)
{
    const char *data[] =
    {
        "word1 word2 , word3",
        "word1 word2 ,word3",
        "word1 word2, word3",
        "word1 word2,word3",
        "word1 word2       ,       word3",
    };
    enum { DATA_SIZE = sizeof(data)/sizeof(data[0]) };
    size_t i;
    for (i = 0; i < DATA_SIZE; i++)
        tester(data[i]);
    return(0);
}
Run Code Online (Sandbox Code Playgroud)

示例输出:

Test data: <<word1 word2 , word3>>
n = 3; w1 = <<word1>>, w2 = <<word2>>, w3 = <<word3>>
Test data: <<word1 word2 ,word3>>
n = 3; w1 = <<word1>>, w2 = <<word2>>, w3 = <<word3>>
Test data: <<word1 word2, word3>>
n = 3; w1 = <<word1>>, w2 = <<word2>>, w3 = <<word3>>
Test data: <<word1 word2,word3>>
n = 3; w1 = <<word1>>, w2 = <<word2>>, w3 = <<word3>>
Test data: <<word1 word2       ,       word3>>
n = 3; w1 = <<word1>>, w2 = <<word2>>, w3 = <<word3>>
Run Code Online (Sandbox Code Playgroud)

一旦'非赋值字符类'只接受逗号,您可以将其缩写为格式字符串中的文字逗号:

int n = sscanf(data, "%19s %31[^, ] , %63s", word1, word2, word3);
Run Code Online (Sandbox Code Playgroud)

将其插入测试安全带会产生与之前相同的结果.请注意,所有代码都受益于审核; 即使在工作之后,它也经常(基本上总是)得到改善.

  • +1:我只是为输入添加一个限制:`... scanf("%99s")...`或`... scanf("%99 [^,]")...`for `char [100]`类型的数组. (2认同)