C语言 sscanf() 函数格式化浅析

标准库函数族

NAME
       scanf, fscanf, sscanf, vscanf, vsscanf, vfscanf - input format conversion

SYNOPSIS
       #include <stdio.h>

       int scanf(const char *format, ...);
       int fscanf(FILE *stream, const char *format, ...);
       int sscanf(const char *str, const char *format, ...);

       #include <stdarg.h>

       int vscanf(const char *format, va_list ap);
       int vsscanf(const char *str, const char *format, va_list ap);
       int vfscanf(FILE *stream, const char *format, va_list ap);

以函数 int sscanf(const char *str, const char *format, ...) 切入,描述格式化规则

  • 该函数根据参数format(格式化字符串)来转换参数str指向的字符串,转换后的结果存于对应的可变参数内。其返回值为按照指定格式转换符成功读入且赋值的可变参数数目(若发生匹配错误而部分成功,该数目会小于指定的参数数目,甚至为0)。若首次成功转换或错误匹配发生前输入已结束(如str为空字符串),则返回EOF。发生读取错误时也返回EOF,且设置错误码errno(如format为空指针时返回EOF并设置errno为EINVAL)。可见,通过比较该函数的返回值与指定的可变参数数目,可判断格式转换是否成功。
  • format可为一个或多个{%[*] [width] [{h | l | L}]type | ' ' | '\t' | '\n' | 非%符号}格式转换符。集合中{a|b|c}表示格式符a、b、c任选其一。以中括号括起来的格式符可选。%type为必选,所有格式符必须以%开头。
  • 赋值抑制符'*'表明按照随后的转换符指示来读取输入,但将其丢弃不予赋值(“跳过”)。抑制符无需相应的指针可变参数,该转换也不计入函数返回的成功赋值次数。%*[width] [{h | l | L}]type 表示满足该条件的字符被过滤掉,不会向目标参数中赋值
  • width表示最大读取宽度。当读入字符数超过该值,或遇到不匹配的字符时,停止读取。多数转换丢弃起始的空白字符。这些被丢弃的字符及转换结果添加的空结束符('\0')均不计入最大读取宽度。
  • {h | l | L}为类型修饰符。h指示输入的数字数值以short int或unsigned short int类型存储;hh指示输入以signed char或unsigned char类型存储。l(小写L)指示输入以long int、unsigned long int或double类型存储,若与%c或%s结合则指示输入以宽字符或宽字符串存储;ll等同L。L指示输入以long long类型存储。
  • type 为类型转换符,如%s、%d。
  • []:字符集合。[]表示指定的字符集合匹配非空的字符序列;^则表示过滤。该操作不会跳过空白字符(空格、制表或换行符),因此可用于目标字符串不以空白字符分隔时。[]内可有一到多个非^字符(含连字符'-'),且无顺序要求。%[a-z]表示匹配a到z之间的任意字符,%[aB-]匹配a、B、-中的任一字符;%[^a]则匹配非a的任意字符,即获取第一个a之前的(不为a的)所有字符。^可作用于多个条件,如^a-z=表示^a-z且^=(既非小写字母亦非等号)。空字符集%[]%[^]会导致不可预知的结果。
  • 使用[]时接收输入的参数必须是有足够存储空间的char、signed char或unsigned char数组。[]也是转换符,故%[]后无s。
  • %[^]的含义和用法与正则表达式相同,故sscanf函数某种程度上提供了简单的正则表达式功能。

格式化示例

原始字符转 格式化字符串 格式化结果 备注
helloworld %s helloworld
hello, world %s hello,
hello, world %4s hell
hello, world %*s%s world 若原字符串无空格则得到空串
hello, world %[^ ] hello,
hello, world %[a-z] hello
12345helloWORLD %[1-9a-z]或%[a-z1-9] 12345hello
12345helloWORLD %[^A-Z] 12345hello
12345helloWORLD %[^a-z] 12345h
12345helloWORLD %[a-z] (空串,需先过滤前面不需要的字符)
12345helloWORLD= %[1-9]%[a-z]%[^a-z=] WORLD
12345/hello@world %*[^/]/%[^@] hello
IpAddr=10.46.44.40 %*[^=]=%s 10.46.44.40
email:wxy@zte.com.cn; %*[^:]:%[^;] wxy@zte.com.cn
email:wxy@zte.com.cn %*[^:]:%s wxy@zte.com.cn
wxy@zte.com.cn %[^@]%*c%s wxy@zte.com.cn
1hello234world5 1%[2]234%[5] 串1:hello 串2:world
Michael/nWang %[^/n]%c%c%s 串1:Michael 串2:Wang
Michael\nWang %[^\n]%c%c%s 串1:Michael 串2:Wang
13:10:29-13:11:08 %[0-9,:]-%[0-9,:] 串1:13:10:29 串2:13:11:08

示例源码

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

#define SIZE 256

void test_format(const char* raw, const char* format, char* buf) {
  sscanf(raw, format, buf);
  printf("%-25s%-20s%-20s", raw, format, buf);
  printf("\n");
  memset(buf, 0, SIZE);
}

void test_format2(const char* raw, const char* format, char* buf1, char* buf2) {
  sscanf(raw, format, buf1, buf2);
  printf("%-25s%-20s%-20s%-20s", raw, format, buf1, buf2);
  printf("\n");
  memset(buf1, 0, SIZE);
  memset(buf2, 0, SIZE);
}

int main(int argc, char* argv[]) {
  char buf1[SIZE] = {0};
  char buf2[SIZE] = {0};

  test_format("helloworld", "%s", buf1);
  test_format("hello, world", "%s", buf1);
  test_format("hello, world", "%4s", buf1);
  test_format("hello, world", "%*s%s", buf1);
  test_format("hello, world", "%[^]", buf1);
  test_format("hello, world", "%[a-z]", buf1);
  test_format("12345helloWORLD", "%[1-9a-z]", buf1);
  test_format("12345helloWORLD", "%[^A-Z]", buf1);
  test_format("12345helloWORLD", "%[^a-z]", buf1);
  test_format("12345helloWORLD", "%[a-z]", buf1);
  test_format("12345/hello@world", "%*[^/]/%[^@]", buf1);
  test_format("IpAddr=10.46.44.40", "%*[^=]=%s", buf1);
  test_format("email:wxy@zte.com.cn;", "%*[^:]:%[^;]", buf1);
  test_format("email:wxy@zte.com.cn", "%*[^:]:%s", buf1);

  test_format2("wxy@zte.com.cn", "%[^@]%*c%s", buf1, buf2);
  test_format2("IpAddr=10.46.44.40", "%[^=]=%s", buf1, buf2);
  test_format2("1hello234world5", "1%[^2]234%[^5]", buf1, buf2);
  test_format2("Michael/nWang", "%[^/n]%*c%*c%s",buf1, buf2);
  test_format2("Michael\\nWang", "%[^\\n]%*c%*c%s", buf1, buf2);
  test_format2("13:10:29-13:11:08", "%[0-9,:]-%[0-9,:]", buf1, buf2);

  return 0;
}

posted @ 2024-01-18 09:26  迦洛德影歌  阅读(76)  评论(0)    收藏  举报