好的,C语言中字符串的读入是一个基础但容易混淆的知识点。不同的函数有不同的特性,适用于不同的场景。我来为你系统地梳理一下,方便你做笔记。
核心概念:字符串在C语言中的存储
C语言没有真正的字符串类型,而是用字符数组来存储字符串,以空字符 '\0' 作为结束标志。任何读入操作都必须确保数组末尾有这个 '\0'。
1. scanf 函数
scanf 是最常用的输入函数,但与字符串一起使用时需要特别注意。
a) 使用 %s 格式
-
特性:
- 从第一个非空白字符开始读取,直到遇到空白字符(空格、制表符、换行符)为止。
- 会自动在末尾添加
'\0'。 - 不安全:如果输入的单词长度超过字符数组的长度,会导致缓冲区溢出,这是严重的漏洞。
-
语法:
char str[100]; scanf("%s", str); // 注意:数组名本身就是地址,所以不需要 &str
-
示例:
#include <stdio.h> int main() { char name[20]; printf("请输入你的名字(不能有空格): "); scanf("%s", name); printf("你好, %s!\n", name); return 0; }
- 输入:
Alice - 输出:
你好, Alice! - 输入:
Alice Smith - 输出:
你好, Alice!(后面的Smith会留在输入缓冲区中)
- 输入:
b) 使用 %[^\n] 格式 (读取带空格的字符串)
-
特性:
%[^\n]表示读取所有字符,直到遇到换行符\n为止。- 这允许你读取包含空格的整行字符串。
- 同样有缓冲区溢出的风险。
-
语法:
char str[100]; scanf("%[^\n]", str); // 读取一行,直到遇到换行符
-
示例:
#include <stdio.h> int main() { char sentence[100]; printf("请输入一句话: "); scanf("%[^\n]", sentence); // 可以读取带空格的输入 printf("你输入的是: %s\n", sentence); return 0; }
- 输入:
I love C programming. - 输出:
你输入的是: I love C programming.
- 输入:
c) 限制读取长度 (安全的方法)
- 方法:在
%s或%[^\n]中间加入数字,指定最多读取的字符数(这个数量需要为'\0'预留一个位置)。 - 语法:
char str[10]; // 最多能存储9个有效字符 + 1个 '\0' scanf("%9s", str); // 最多读取9个字符,确保不会溢出
2. gets 函数 (已废弃,绝对不要使用!)
- 特性:
- 读取一整行,直到遇到换行符,然后丢弃换行符并添加
'\0'。 - 极其危险:它不知道目标数组的大小,会无限制地读取,极易导致缓冲区溢出。
- 读取一整行,直到遇到换行符,然后丢弃换行符并添加
- 在现代编译器中,使用
gets通常会收到警告或错误。
3. fgets 函数 (推荐的读行方法)
这是读取整行字符串最安全、最标准的方法。
-
特性:
- 会指定读取的最大字符数和目标流(通常是标准输入
stdin)。 - 会读取并存储换行符
\n(如果数组空间足够容纳它)。 - 绝对保证不会发生缓冲区溢出。
- 会指定读取的最大字符数和目标流(通常是标准输入
-
语法:
char *fgets(char *str, int n, FILE *stream); // str: 目标字符数组 // n: 最大读取字符数(包括最后的 '\0') // stream: 输入流,使用 stdin 表示从标准输入读取
-
示例:
#include <stdio.h> int main() { char line[100]; printf("请输入一行文字: "); fgets(line, sizeof(line), stdin); // 安全地读取最多99个字符 printf("你输入的是: %s", line); // 注意,line中可能包含换行符 return 0; }
- 输入:
Hello, how are you? - 输出:
你输入的是: Hello, how are you?(后面自带一个换行)
- 输入:
-
处理
fgets的换行符:
很多时候我们不希望字符串末尾有换行符,可以手动移除它。#include <stdio.h> #include <string.h> int main() { char line[100]; printf("请输入: "); fgets(line, sizeof(line), stdin); // 移除换行符:找到换行符并将其替换为 '\0' size_t len = strlen(line); if (len > 0 && line[len-1] == '\n') { line[len-1] = '\0'; } printf("处理后的字符串: \"%s\"\n", line); return 0; }
4. getchar 函数 (逐个字符读取)
用于需要精细控制输入流程的场景,例如自己实现一个读行函数。
- 示例:用
getchar模拟fgets#include <stdio.h> void my_gets(char *str, int max_len) { int i = 0; char ch; while (i < max_len - 1 && (ch = getchar()) != '\n' && ch != EOF) { str[i++] = ch; } str[i] = '\0'; // 不要忘记添加字符串结束符 } int main() { char str[100]; printf("请输入: "); my_gets(str, sizeof(str)); printf("你输入的是: %s\n", str); return 0; }
总结与对比表
| 方法 | 能否读空格? | 是否安全? | 是否读取换行符? | 适用场景 |
|---|---|---|---|---|
scanf("%s") |
不能 | 不安全 | 不读取 | 读取单个单词 |
scanf("%[^\n]") |
能 | 不安全 | 不读取 | 读取一行(不包含换行符) |
gets |
能 | 极不安全 | 读取但丢弃 | 绝对不要使用 |
fgets |
能 | 安全 | 读取并存储 | 读取一行的首选方法 |
getchar |
能 | 安全(取决于实现) | 可控制 | 需要逐个字符处理的复杂输入 |
最佳实践建议
- 读取单个无空格的单词:使用
scanf并限制长度,如scanf("%9s", str)。 - 读取一行包含空格的字符串:总是使用
fgets。 - 永远不要使用
gets。 - 使用
fgets后,如果不需要换行符,记得手动移除。 - 在调用
fgets读取一行后,如果想再用scanf,要注意清理输入缓冲区,因为fgets和scanf对换行符的处理方式不同,可能会互相干扰。
希望这份详细的笔记对你有帮助!