scanf/sscanf %[]格式控制串的用法

本文介绍scanf函数中较少为人知的%[]特性,它能够读取指定字符集直至遇到不符合条件的字符。通过具体示例展示了如何利用%[]进行灵活的字符串输入控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

scanf中一种很少见但很有用的转换字符:[...]和[ ^...]。
#include<stdio.h>
int main()
{
char strings[100];
scanf("%[1234567890]",strings);
printf("%s",strings);
return  0;
}
运行,输入:1234werew后,结果是:1234。
通过运行可以发现它的作用是:如果输入的字符属于方括号内字符串中某个字符,那么就提取该字符;如果一经发现不属于就结束提取。该方法会自动加上一个字符串结束符到已经提取的字符后面。
scanf("%[^1234567890]",strings); 它的作用是:如果一经发现输入的字符属于方括号内字符串中某个字符,那么就结束提取;如果不属于就提取该字符。该方法会自动加上一个字符串结束符到已经提取的字符后面。
注意:方括号两边不能空格,如:scanf("%[ 1234567890 ]",strings); scanf("%[ ^1234567890 ]",strings); 不让空格也会算在里面的。
用这种方法还可以解决scanf的输入中不能有空格的问题。只要用
scanf("%[^/n]",strings); 就可以了。很神奇吧。

  ANSI C 标准向 scanf() 增加了一种新特性,称为扫描集(scanset)。 扫描集定义一个字符集合,可由 scanf() 读入其中允许的字符并赋给对应字符数组。 扫描集合由一对方括号中的一串字符定义,左方括号前必须缀以百分号。 例如,以下的扫描集使 scanf() 读入字符 A、B 和 C:
    %[ABC]

    使用扫描集时,scanf() 连续吃进集合中的字符并放入对应的字符数组,直到发现不在集合中的字符为止(即扫描集仅读匹配的字符)。返回时,数组中放置以 null 结尾、由读入字符组成的字符串。

    用字符 ^ 可以说明补集。把 ^ 字符放为扫描集的第一字符时,构成其它字符组成的命令的补集合,指示 scanf() 只接受未说明的其它字符。
    对于许多实现来说,用连字符可以说明一个范围。 例如,以下扫描集使 scanf() 接受字母 A 到 Z:
    %[A-Z]
    重要的是要注意扫描集是区分大小写的。因此,希望扫描大、小写字符时,应该分别说明大、小写字母。


搜集一些特殊用法:

 

%[ ] 的用法:%[ ]表示要读入一个字符集合, 如果[ 后面第一个字符是”^”,则表示反意思。

                     [ ]内的字符串可以是1或更多字符组成。空字符集(%[])是违反规定的,可

                     导致不可预知的结果。%[^]也是违反规定的。
          

%[a-z] 读取在 a-z 之间的字符串,如果不在此之前则停止,如

              char s[]=
"hello, my friend” ; // 注意: ,逗号在不 a-z之间

              sscanf( s, “%[a-z]”, string ) ; // string=hello


%[^a-z] 读取不在 a-z 之间的字符串,如果碰到a-z之间的字符则停止,如

              char s[]="
HELLOkitty” ;
// 注意: ,逗号在不 a-z之间


              sscanf( s,%[^a-z], string ) ;
// string=HELLO



%*[^=] 前面带 * 号表示不保存变量。跳过符合条件的字符串。

              char s[]="notepad=1.0.0.1001" ;

       char szfilename [32] = "" ;

       int i = sscanf( s, "%*[^=]", szfilename ) ;

// szfilename=NULL,因为没保存


int i = sscanf( s, "%*[^=]=%s", szfilename ) ;

// szfilename=1.0.0.1001


%40c 读取40个字符


%[^=] 读取字符串直到碰到’=’号,’^’后面可以带更多字符,如:

              char s[]="notepad=1.0.0.1001" ;

              char szfilename [32] = "" ;

             int i = sscanf( s, "%[^=]", szfilename ) ;

           // szfilename=notepad


       如果参数格式是:%[^=:] ,那么也可以从 notepad:1.0.0.1001读取notepad

<think>我们被要求对比C语言中fgets、scanfsscanf函数的用法及返回值处理。根据用户提供的引用和之前的历史回答,我们可以进行如下分析: 1. **函数基本用途**: - `fgets`:主要用于从文件流(包括标准输入)中读取一行字符,可以包含空格,并且会读取换行符(如果缓冲区足够)。 - `scanf`:格式化输入函数,从标准输入读取数据,根据格式字符解析数据,遇到空格、制表符或换行符会停止读取(取决于格式说明符)。 - `sscanf`:从字符中按照格式读取数据,类似于scanf,但是输入源是字符而不是标准输入。 2. **安全性**: - `fgets`相对安全,因为它可以指定读取的最大字符数,避免缓冲区溢出。 - `scanf`在使用`%s`等格式时容易造成缓冲区溢出,除非指定宽度(如`%9s`)。 - `sscanf`由于从字符中读取,所以相对安全,但也要注意目标缓冲区的大小。 3. **空格和换行处理**: - `fgets`会读取一行,包括空格和换行符(如果缓冲区足够大)。 - `scanf`在读取字符(`%s`)时遇到空格或换行符会停止,而`%c`可以读取空格和换行符。 - `sscanf`的行为与`scanf`类似,但输入源是字符。 4. **返回值**: - `fgets`:成功时返回传入的字符指针,失败或到达文件末尾返回NULL。 - `scanf`:返回成功匹配并赋值的输入项的数量,如果到达文件末尾则返回EOF。 - `sscanf`:返回成功匹配并赋值的输入项的数量。 5. **使用场景**: - `fgets`:适合读取一行文本,尤其是包含空格的字符。 - `scanf`:适合读取格式化的输入,例如整数、浮点数或没有空格的字符。 - `sscanf`:适合从一个已经读取的字符中提取格式化数据。 6. **返回值处理**: - 对于`fgets`,通常检查返回值是否为NULL来判断是否成功读取。 - 对于`scanf`,需要检查返回值是否等于预期的参数个数,以判断是否所有输入都成功读取。 - 对于`sscanf`,同样需要检查返回值是否等于预期的参数个数。 下面我们按照要求进行详细对比,并给出代码示例。 ### 1. 函数原型与基本用法 #### fgets ```c char *fgets(char *str, int n, FILE *stream); ``` - `str`:指向字符数组的指针,用于存储读取的数据。 - `n`:要读取的最大字符数(包括最后的空字符)。 - `stream`:文件流,如标准输入流`stdin`。 示例: ```c char buffer[100]; fgets(buffer, sizeof(buffer), stdin); ``` #### scanf ```c int scanf(const char *format, ...); ``` - `format`:格式控制字符。 - `...`:可变参数,为要存储数据的变量地址。 示例: ```c int num; scanf("%d", &num); ``` #### sscanf ```c int sscanf(const char *str, const char *format, ...); ``` - `str`:从中读取数据的字符。 - `format`:格式控制字符。 - `...`:可变参数,为要存储数据的变量地址。 示例: ```c char str[] = "123"; int num; sscanf(str, "%d", &num); ``` ### 2. 关键特性对比 | 特性 | `fgets` | `scanf` | `sscanf` | |------|---------|---------|----------| | **输入源** | 文件流(如`stdin`) | 标准输入 | 字符 | | **读取方式** | 行读取(包括换行符) | 格式化读取(按格式字符) | 格式化读取(按格式字符) | | **空格处理** | 读取空格和换行符(直到遇到换行符或EOF) | 根据格式字符:`%s`跳过前导空白,遇到空白停止;`%c`读取任意字符(包括空白) | 同`scanf` | | **安全性** | 高(可指定缓冲区大小) | 低(易缓冲区溢出,除非指定宽度) | 中等(从字符读取,但也要注意目标缓冲区) | | **返回值** | 成功:`str`;失败或EOF:`NULL` | 成功赋值的输入项数量;错误或EOF:`EOF` | 成功赋值的输入项数量 | | **换行符处理** | 保留在字符中(如果缓冲区足够) | 通常留在输入缓冲区,可能影响后续输入 | 不涉及(输入源为字符) | | **常见用途** | 读取一行文本 | 读取格式化输入(如数字、单词) | 解析字符中的格式化数据 | ### 3. 返回值处理详解 - **`fgets`返回值处理**: 通常检查返回值是否为`NULL`,如果为`NULL`则表示到达文件末尾或发生错误。 ```c if (fgets(buffer, sizeof(buffer), stdin) == NULL) { // 处理错误或EOF } ``` - **`scanf`返回值处理**: 检查返回值是否等于预期的参数个数。例如,读取一个整数时,返回值应为1;读取两个整数时,返回值应为2。如果返回值小于预期,说明输入不匹配或到达文件末尾。 ```c int a, b; int ret = scanf("%d %d", &a, &b); if (ret != 2) { // 处理错误:输入不匹配或EOF } ``` - **`sscanf`返回值处理**: 与`scanf`类似,检查返回值是否等于预期的参数个数。返回值表示成功匹配并赋值的参数个数。 ```c char str[] = "123 456"; int a, b; int ret = sscanf(str, "%d %d", &a, &b); if (ret != 2) { // 处理错误:解析失败 } ``` ### 4. 代码示例:组合使用fgets和sscanf 由于`scanf`直接读取用户输入可能遇到换行符残留问题,而`fgets`可以安全读取一行,因此常用组合是先用`fgets`读取一行,再用`sscanf`解析。 ```c #include <stdio.h> #include <string.h> int main() { char line[100]; int num1, num2; // 使用fgets读取一行 if (fgets(line, sizeof(line), stdin) == NULL) { printf("读取失败\n"); return 1; } // 移除换行符(如果存在) line[strcspn(line, "\n")] = '\0'; // 使用sscanf解析 if (sscanf(line, "%d %d", &num1, &num2) != 2) { printf("解析失败\n"); return 1; } printf("num1=%d, num2=%d\n", num1, num2); return 0; } ``` ### 5. 总结与建议 - **安全性**:优先使用`fgets`读取用户输入,避免缓冲区溢出。 - **灵活性**:使用`sscanf`解析字符,结合`fgets`可以安全地处理各种输入。 - **格式化输入**:当需要简单读取格式化数据(如单个整数)且输入可靠时,可以使用`scanf`,但要注意限制宽度(如`%99s`)并处理返回值。 - **返回值处理**:始终检查这些函数的返回值,以确保输入正确。 通过以上对比,我们可以根据实际需求选择合适的函数:`fgets`用于安全读取一行,`scanf`用于简单格式化输入(需谨慎),`sscanf`用于解析字符。 §§相关问题§§ 1. 如何避免使用scanf时的缓冲区溢出问题? 2. 使用fgets读取后如何正确处理换行符? 3. sscanf函数支持哪些高级格式匹配(如正则表达式)?</think>### C语言中fgets、scanfsscanf函数用法对比及返回值处理 #### 1. **函数功能与使用场景** | 函数 | 输入源 | 核心功能 | 典型应用场景 | |----------|----------------|----------------------------|-------------------------------| | **fgets** | 文件流/标准输入 | 安全读取整行文本 | 用户输入、文件行读取[^1] | | **scanf** | 标准输入 | 格式化输入解析 | 简单数据类型输入(整数/浮点数)[^3] | | **sscanf** | 字符 | 从字符解析格式化数据 | 字符数据转换、协议解析[^4] | #### 2. **关键特性对比** | 特性 | fgets | scanf | sscanf | |------------------|---------------------------|----------------------------|----------------------------| | **空格处理** | 保留空格和换行符 | `%s`跳过空格,`%c`读取空格 | 同scanf | | **缓冲区安全** | ✅ 指定最大读取长度 | ❌ 易溢出(需手动限制) | ✅ 从固定字符读取 | | **换行符处理** | 包含`\n`(需手动移除) | 残留输入流(需清空) | 不涉及 | | **返回值意义** | 成功:字符指针<br>失败:NULL | 成功赋值的参数个数 | 成功赋值的参数个数 | | **格式灵活性** | 低(仅文本) | 高(支持多种格式符) | 最高(支持正则表达式)[^4] | #### 3. **返回值处理详解** ```c // ===== fgets返回值处理 ===== char buffer[100]; if (fgets(buffer, sizeof(buffer), stdin) == NULL) { // 处理错误或EOF } else { // 移除可能的换行符 buffer[strcspn(buffer, "\n")] = '\0'; } // ===== scanf返回值处理 ===== int num1, num2; int count = scanf("%d %d", &num1, &num2); if (count == 2) { // 成功读取两个整数 } else if (count == EOF) { // 文件结束 } else { // 输入不匹配,清空缓冲区 while (getchar() != '\n'); } // ===== sscanf返回值处理 ===== char data[] = "2023-08-15 12:30:00"; int year, month, day, hour, min, sec; if (sscanf(data, "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &min, &sec) == 6) { // 成功解析所有字段 } else { // 格式不匹配处理 } ``` #### 4. **组合使用最佳实践** ```c #include <stdio.h> #include <string.h> int main() { char input[100]; float price; int quantity; // 安全读取用户输入 if (fgets(input, sizeof(input), stdin)) { // 移除换行符 input[strcspn(input, "\n")] = 0; // 从字符解析数据 if (sscanf(input, "%f %d", &price, &quantity) == 2) { printf("总价: $%.2f\n", price * quantity); } else { printf("输入格式错误!示例: 19.99 3\n"); } } return 0; } ``` *组合优势*: 1. `fgets`防止缓冲区溢出 2. `sscanf`避免输入流残留问题 3. 精确控制错误处理流程 #### 5. **高级格式解析示例(sscanf)** ```c char log[] = "Error[2023-08-15]: File not found"; char err_type[20], date[11], message[50]; // 使用正则表达式提取结构化数据 if (sscanf(log, "%19[^[][%10[^]]]: %49[^\n]", err_type, date, message) == 3) { printf("类型: %s\n日期: %s\n信息: %s\n", err_type, date, message); } ``` *输出*: ``` 类型: Error 日期: 2023-08-15 信息: File not found ``` #### 6. **总结与推荐** | 场景 | 推荐函数 | 原因 | |--------------------------|---------------|----------------------------------------------------------------------| | 用户输入/文件读取 | **fgets** | 安全可靠,避免缓冲区溢出[^1] | | 简单格式输入(已知无空格) | **scanf** | 代码简洁,直接绑定变量[^3] | | 字符数据解析 | **sscanf** | 支持复杂格式,正则表达式提取能力强[^4] | | 安全关键系统 | **fgets+sscanf** | 双重安全保障,精确控制解析过程 | > **关键建议**: > 1. 永远不要使用`gets()` - 已被弃用且极度危险 > 2. 使用`scanf`时务必指定宽度:`scanf("%99s", buf)` > 3. 解析后检查返回值,匹配预期参数数量 > 4. 组合使用`fgets+sscanf`是最安全的输入处理方案[^2]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值