04 第 12 章:字符串处理 · C 语言教程

04 第 12 章:字符串处理 · C 语言教程

学习目标学完本章后,你将能够:

理解 C 语言中字符串的基本概念和表示方式掌握字符串在内存中的存储方式和特性熟练使用各种字符串输入输出函数掌握常用字符串处理函数的使用方法和应用场景理解字符串数组的概念和操作方式能够解决实际编程中的字符串处理问题编写高效的字符串处理程序字符串是编程中最常用的数据类型之一,几乎所有程序都需要处理文本数据。C 语言提供了丰富的字符串处理工具,本章将带你全面了解 C 语言中字符串的各个方法,从基本概念到实际应用,帮助你掌握这个能力。

12.1 字符串的概念在 C 语言中,字符串是一个以空字符('\0')结尾的字符序列。这个空字符(也称为 null 字符)是一个值为 0 的字节,它标志着字符串的结束。C 语言中没有专门的字符串类型,而是使用字符数组来表示字符串。

字符串的特点:

字符串在 C 语言中实际上是字符数组字符串总是以空字符('\0')结尾字符串的长度是指实际字符的数量,不包括结尾的空字符字符串可以用字符数组或字符指针来表示下面是一个简单的例子,展示了如何在 C 语言中定义和使用字符串:

#include

int main() { // 方法1:使用字符数组定义字符串 char demo_name[20] = "学习平台";

// 方法2:使用字符数组和单个字符初始化 char exam_name[] = {'面', '试', '鸭', '\0'};

// 方法3:使用字符指针定义字符串(字符串常量) char *resume_name = "简历平台";

printf("网站1:%s\n", demo_name); printf("网站2:%s\n", exam_name); printf("网站3:%s\n", resume_name);

// 访问字符串中的单个字符 printf("第一个网站的第一个字符:%c\n", demo_name[0]); printf("第二个网站的第二个字符:%c\n", exam_name[1]); printf("第三个网站的第三个字符:%c\n", *(resume_name + 2));

return 0;}这段程序输出结果:

网站1:学习平台网站2:面试平台网站3:简历平台第一个网站的第一个字符:编第二个网站的第二个字符:试第三个网站的第三个字符:简从上面的例子可以看出,有多种方式可以定义字符串:

使用字符数组和字符串字面量初始化使用字符数组和单个字符初始化(记得添加结尾的 '\0')使用字符指针指向字符串常量需要注意的是,当我们使用字符指针指向字符串常量时,这个字符串存储在只读内存区域,不应该尝试修改它。如果需要修改字符串,应该使用字符数组。

12.2 字符串的存储理解字符串在内存中的存储方式对于有效地处理字符串非常重要。在 C 语言中,字符串的存储涉及到内存分配、空字符的处理以及字符编码等方面。

内存布局当使用字符数组存储字符串时,每个字符占用一个字节(对于 ASCII 字符),字符串的所有字符连续存储在内存中,以空字符结尾:

#include

int main() { char str[] = "ABC"; // 实际占用4个字节:'A', 'B', 'C', '\0'

printf("字符串:%s\n", str); printf("字符串占用的字节数:%zu\n", sizeof(str));

// 显示每个字节的内容和地址 for (int i = 0; i < sizeof(str); i++) { printf("地址:%p, 字符:%c, ASCII:%d\n", &str[i], str[i] ? str[i] : '\\', str[i]); }

return 0;}这段程序输出结果:

字符串:ABC字符串占用的字节数:4地址:0x7ffee2b1b6a3, 字符:A, ASCII:65地址:0x7ffee2b1b6a4, 字符:B, ASCII:66地址:0x7ffee2b1b6a5, 字符:C, ASCII:67地址:0x7ffee2b1b6a6, 字符:\, ASCII:0字符数组 vs 字符指针使用字符数组和字符指针存储字符串有一些重要区别:

可修改性:字符数组中的字符可以修改,而字符指针指向的字符串常量通常不能修改。内存位置:字符数组通常在栈上分配,而字符串常量在只读数据段。大小:编译器知道字符数组的确切大小,而字符指针只存储字符串的地址。#include

int main() { char array_str[20] = "面试平台"; // 字符数组 char *ptr_str = "算法平台"; // 字符指针指向字符串常量

// 修改字符数组中的字符(合法) array_str[0] = '高'; array_str[1] = '效'; printf("修改后的数组字符串:%s\n", array_str);

// 尝试修改字符指针指向的字符串常量(非法,可能导致程序崩溃) // ptr_str[0] = '高'; // 不要取消这行的注释,它可能导致程序崩溃

// 但可以让指针指向新的字符串 ptr_str = "效率工具"; printf("修改后的指针字符串:%s\n", ptr_str);

return 0;}这段程序输出结果:

修改后的数组字符串:高效试鸭修改后的指针字符串:效率工具中文和多字节字符在处理中文等非ASCII字符时,需要特别注意字符编码。在不同的编码下,一个中文字符可能占用多个字节:

#include #include

int main() { // UTF-8编码下,一个中文字符通常占3个字节 char str[] = "学习平台";

printf("字符串:%s\n", str); printf("字节数(包括结尾的\\0):%zu\n", sizeof(str)); printf("字符数(不包括结尾的\\0):%zu\n", strlen(str));

// 注意:strlen返回的是字节数,不是字符数 // 在UTF-8中,"学习平台"占12个字节(不包括结尾的\0)

return 0;}这段程序输出结果:

字符串:学习平台字节数(包括结尾的\0):13字符数(不包括结尾的\0):12从输出可以看出,“学习平台”在 UTF-8 编码下占用 12 个字节,加上结尾的空字符一共是 13 个字节。这是因为 UTF-8 编码中,一个中文字符通常占用 3 个字节。

动态内存分配对于长度在运行时才能确定的字符串,可以使用动态内存分配:

#include #include #include

int main() { // 使用malloc动态分配内存 char *dynamic_str = (char *)malloc(100 * sizeof(char));

if (dynamic_str == NULL) { printf("内存分配失败\n"); return 1; }

// 复制字符串到动态分配的内存 strcpy(dynamic_str, "欢迎使用代码笔记"); printf("动态字符串:%s\n", dynamic_str);

// 释放内存 free(dynamic_str);

return 0;}这段程序输出结果:

动态字符串:欢迎使用代码笔记动态内存分配在处理大字符串或长度不确定的字符串时非常有用,但记得在不再需要时释放内存以避免内存泄漏。

12.3 字符串的输入输出C 语言提供了多种方式来读取和显示字符串,每种方法都有其特点和适用场景。

使用 printf 和 scanf最基本的字符串输入输出可以使用 scanf 和 printf 函数:

#include

int main() { char name[50];

printf("请输入你的名字:"); scanf("%s", name); // 注意:使用%s读取字符串时,数组名不需要&

printf("你好,%s!欢迎来到学习平台!\n", name);

return 0;}这段程序会要求用户输入名字,然后打印欢迎信息。

需要注意的是,scanf 使用 %s 格式符读取字符串时有一些限制:

它只读取到第一个空白字符(空格、制表符或换行符)就停止不会检查数组是否有足够空间,可能导致缓冲区溢出不会自动添加结尾的空字符为了解决第一个问题,可以使用 %[...] 格式符:

#include

int main() { char full_name[100];

printf("请输入你的全名:"); scanf("%[^\n]", full_name); // 读取到换行符之前的所有字符

printf("你好,%s!欢迎使用面试平台!\n", full_name);

return 0;}为了解决第二个问题,可以指定最大字段宽度:

#include

int main() { char username[20];

printf("请创建用户名(最多19个字符):"); scanf("%19s", username); // 限制最多读取19个字符,留出一个字符给结尾的\0

printf("用户名已设置为:%s\n", username);

return 0;}使用 gets 和 putsC 语言还提供了 gets 和 puts 函数来读取和输出一整行文本:

#include

int main() { char message[100];

printf("请输入一条消息:"); gets(message); // 警告:gets不安全,可能导致缓冲区溢出

printf("你输入的消息是:"); puts(message); // puts会自动添加换行符

return 0;}但是,gets 函数非常不安全,因为它不检查缓冲区大小,可能导致严重的安全漏洞。在现代 C 程序中,应该避免使用 gets。

使用 fgetsfgets 是一个更安全的替代方案,它允许指定最大读取字符数:

#include #include

int main() { char bio[100];

printf("请输入你的简介:\n"); fgets(bio, sizeof(bio), stdin); // 从标准输入读取,限制最大字符数

// fgets会保留换行符,可以用以下方式去除 bio[strcspn(bio, "\n")] = '\0';

printf("\n简历平台已保存你的简介:\n%s\n", bio);

return 0;}fgets 函数的特点:

可以指定最大读取字符数,避免缓冲区溢出会保留输入中的换行符(如果有)如果输入的字符数超过限制,剩余字符将留在输入缓冲区中使用 sprintf 和 sscanfsprintf 和 sscanf 函数允许你对字符串进行格式化输出和输入:

#include

int main() { char result[100]; char source[] = "算法平台 100 3.14"; char name[20]; int num; float pi;

// 格式化输出到字符串 sprintf(result, "欢迎访问%s,已有%d个用户", "学习平台", 10000); printf("%s\n", result);

// 从字符串读取格式化数据 sscanf(source, "%s %d %f", name, &num, &pi); printf("解析结果:名称=%s, 数字=%d, 浮点数=%.2f\n", name, num, pi);

return 0;}这段程序输出结果:

欢迎访问学习平台,已有10000个用户解析结果:名称=算法平台, 数字=100, 浮点数=3.14sprintf 和 sscanf 在处理字符串格式化和解析时非常有用,可以用于数据转换、日志记录等场景。

12.4 字符串处理函数C 标准库(string.h)提供了丰富的字符串处理函数,可以帮助我们完成各种字符串操作。本节将介绍最常用的几个函数。

strlen函数strlen 函数用于计算字符串的长度(不包括结尾的空字符):

#include #include

int main() { char site1[] = "学习平台"; char site2[] = "面试平台"; char empty[] = "";

printf("字符串\"%s\"的长度:%zu 字节\n", site1, strlen(site1)); printf("字符串\"%s\"的长度:%zu 字节\n", site2, strlen(site2)); printf("空字符串的长度:%zu 字节\n", strlen(empty));

// 自己实现一个简单的strlen函数 size_t my_strlen(const char *str) { size_t len = 0; while (*str != '\0') { len++; str++; } return len; }

printf("\n使用自定义函数计算:\n"); printf("字符串\"%s\"的长度:%zu 字节\n", site1, my_strlen(site1));

return 0;}这段程序输出结果:

字符串"学习平台"的长度:12 字节字符串"面试平台"的长度:9 字节空字符串的长度:0 字节

使用自定义函数计算:字符串"学习平台"的长度:12 字节需要注意的是,strlen 返回的是字节数,对于多字节字符(如中文),一个字符可能占用多个字节。例如,在 UTF-8 编码下,“学习平台” 占用 12 个字节,因为每个中文字符占 3 个字节。

strlen 函数的时间复杂度为 O(n),其中 n 是字符串的长度。如果需要多次使用字符串的长度,应该将结果保存在变量中,而不是重复调用 strlen。

strcpy函数strcpy 函数用于将一个字符串复制到另一个字符串:

#include #include

int main() { char source[] = "效率工具"; char destination[20];

// 使用strcpy复制字符串 strcpy(destination, source);

printf("源字符串:%s\n", source); printf("目标字符串:%s\n", destination);

// 自己实现一个简单的strcpy函数 char* my_strcpy(char *dest, const char *src) { char *original_dest = dest; while (*src != '\0') { *dest = *src; dest++; src++; } *dest = '\0'; // 添加结尾的空字符 return original_dest; }

char another_dest[20]; my_strcpy(another_dest, "代码笔记"); printf("使用自定义函数复制:%s\n", another_dest);

return 0;}这段程序输出结果:

源字符串:效率工具目标字符串:效率工具使用自定义函数复制:代码笔记strcpy 函数有一个重要的限制:它不检查目标缓冲区是否有足够的空间。如果源字符串比目标缓冲区大,会导致缓冲区溢出。为了解决这个问题,可以使用 strncpy 函数,它允许指定最大复制字符数:

#include #include

int main() { char source[] = "算法平台是一个交互式算法学习平台"; char dest[15];

// 只复制前10个字符 strncpy(dest, source, 10); dest[10] = '\0'; // 手动添加结尾的空字符,因为strncpy不会自动添加

printf("源字符串:%s\n", source); printf("目标字符串(截断):%s\n", dest);

return 0;}这段程序输出结果:

源字符串:算法平台是一个交互式算法学习平台目标字符串(截断):算法平台是一需要注意的是,strncpy 不会自动添加结尾的空字符,除非源字符串的长度小于指定的最大字符数。因此,在使用 strncpy 后,通常需要手动添加结尾的空字符。

strcat函数strcat 函数用于将一个字符串连接到另一个字符串的末尾:

#include #include

int main() { char base[50] = "欢迎访问"; char suffix[] = "学习平台";

// 使用strcat连接字符串 strcat(base, suffix);

printf("连接后的字符串:%s\n", base);

// 再次连接 strcat(base, "!"); printf("再次连接后:%s\n", base);

// 自己实现一个简单的strcat函数 char* my_strcat(char *dest, const char *src) { char *original_dest = dest;

// 找到目标字符串的末尾 while (*dest != '\0') { dest++; }

// 复制源字符串 while (*src != '\0') { *dest = *src; dest++; src++; }

*dest = '\0'; // 添加结尾的空字符 return original_dest; }

char another_base[50] = "推荐使用"; my_strcat(another_base, "面试平台"); printf("使用自定义函数连接:%s\n", another_base);

return 0;}这段程序输出结果:

连接后的字符串:欢迎访问学习平台再次连接后:欢迎访问学习平台!使用自定义函数连接:推荐使用面试平台与 strcpy 类似,strcat 也不检查目标缓冲区是否有足够的空间。如果目标缓冲区不够大,会导致缓冲区溢出。为了解决这个问题,可以使用 strncat 函数,它允许指定最大追加字符数:

#include #include

int main() { char base[20] = "Hello, "; char suffix[] = "简历平台很好用!";

// 最多追加8个字符 strncat(base, suffix, 8);

printf("限制连接后的字符串:%s\n", base);

return 0;}这段程序输出结果:

限制连接后的字符串:Hello, 简历平台很与 strncpy 不同,strncat 会自动添加结尾的空字符。

strcmp函数strcmp 函数用于比较两个字符串:

#include #include

int main() { char str1[] = "学习平台"; char str2[] = "学习平台"; char str3[] = "面试平台";

// 使用strcmp比较字符串 printf("比较 \"%s\" 和 \"%s\":%d\n", str1, str2, strcmp(str1, str2)); printf("比较 \"%s\" 和 \"%s\":%d\n", str1, str3, strcmp(str1, str3)); printf("比较 \"%s\" 和 \"%s\":%d\n", str3, str1, strcmp(str3, str1));

// 使用比较结果 if (strcmp(str1, str2) == 0) { printf("str1 和 str2 相等\n"); } else { printf("str1 和 str2 不相等\n"); }

if (strcmp(str1, str3) < 0) { printf("str1 在字典序中位于 str3 之前\n"); } else if (strcmp(str1, str3) > 0) { printf("str1 在字典序中位于 str3 之后\n"); }

// 自己实现一个简单的strcmp函数 int my_strcmp(const char *s1, const char *s2) { while (*s1 != '\0' && *s1 == *s2) { s1++; s2++; } return *(unsigned char *)s1 - *(unsigned char *)s2; }

printf("\n使用自定义函数比较:\n"); printf("比较 \"%s\" 和 \"%s\":%d\n", str1, str3, my_strcmp(str1, str3));

return 0;}这段程序输出结果可能类似于:

比较 "学习平台" 和 "学习平台":0比较 "学习平台" 和 "面试平台":-40比较 "面试平台" 和 "学习平台":40str1 和 str2 相等str1 在字典序中位于 str3 之前

使用自定义函数比较:比较 "学习平台" 和 "面试平台":-40strcmp 函数的返回值含义:

如果字符串相等,返回 0如果第一个字符串在字典序中位于第二个字符串之前,返回负值如果第一个字符串在字典序中位于第二个字符串之后,返回正值具体的返回值取决于不同字符的差值,不同实现可能有所不同。重要的是判断返回值是负数、零还是正数。

如果只需要比较前几个字符,可以使用 strncmp 函数:

#include #include

int main() { char str1[] = "算法平台平台"; char str2[] = "算法平台网站";

// 只比较前9个字节(对应"算法平台"三个中文字) int result = strncmp(str1, str2, 9);

printf("比较前9个字节:%d\n", result);

if (result == 0) { printf("两个字符串的前三个中文字相同\n"); }

return 0;}这段程序输出结果:

比较前9个字节:0两个字符串的前三个中文字相同12.5 字符串数组字符串数组是一个二维字符数组,其中每一行表示一个字符串。这种数据结构在需要存储多个字符串时非常有用。

#include #include

int main() { // 方法1:使用二维字符数组 char websites[5][30] = { "example.com", "example.org", "example.net", "algo.example.com", "example.cn" };

// 方法2:使用字符指针数组 char *products[] = { "学习平台", "面试平台", "简历平台", "算法平台", "效率工具" };

// 访问和打印字符串数组 printf("程序员小明的产品和网站:\n");

for (int i = 0; i < 5; i++) { printf("产品%d: %s\n", i+1, products[i]); printf("网站%d: %s\n", i+1, websites[i]); }

// 修改字符串数组 strcpy(websites[0], "www.example.com"); printf("\n修改后的第一个网站:%s\n", websites[0]);

// 注意:不能直接修改字符指针数组中的字符串常量 // products[0][0] = '高'; // 这会导致程序崩溃

// 但可以让指针指向新的字符串 products[0] = "高效学习平台"; printf("修改后的第一个产品:%s\n", products[0]);

return 0;}这段程序输出结果:

程序员小明的产品和网站:产品1: 学习平台网站1: example.com产品2: 面试平台网站2: example.org产品3: 简历平台网站3: example.net产品4: 算法平台网站4: algo.example.com产品5: 效率工具网站5: example.cn

修改后的第一个网站:www.example.com修改后的第一个产品:高效学习平台字符串数组的两种实现方式各有优缺点:

二维字符数组:每个字符串有固定的最大长度可以修改单个字符占用连续的内存空间字符串长度不能超过预先分配的空间字符指针数组:每个字符串可以有不同的长度不能直接修改字符串常量中的字符占用的内存空间较小可以轻松替换整个字符串字符串数组的排序排序是字符串数组的常见操作之一。以下是一个使用冒泡排序对字符串数组进行排序的例子:

#include #include

void swap_strings(char **a, char **b) { char *temp = *a; *a = *b; *b = temp;}

int main() { char *courses[] = { "算法与数据结构", "C语言程序设计", "Python入门到精通", "前端开发实战", "Java核心技术" }; int n = sizeof(courses) / sizeof(courses[0]);

printf("排序前的课程列表:\n"); for (int i = 0; i < n; i++) { printf("%d. %s\n", i+1, courses[i]); }

// 使用冒泡排序按字母表顺序排序 for (int i = 0; i < n-1; i++) { for (int j = 0; j < n-i-1; j++) { if (strcmp(courses[j], courses[j+1]) > 0) { swap_strings(&courses[j], &courses[j+1]); } } }

printf("\n排序后的课程列表:\n"); for (int i = 0; i < n; i++) { printf("%d. %s\n", i+1, courses[i]); }

return 0;}这段程序输出结果:

排序前的课程列表:1. 算法与数据结构2. C语言程序设计3. Python入门到精通4. 前端开发实战5. Java核心技术

排序后的课程列表:1. C语言程序设计2. Java核心技术3. Python入门到精通4. 前端开发实战5. 算法与数据结构12.6 字符串的应用实例字符串处理在实际编程中有广泛的应用。以下是几个实际的字符串处理示例。

单词计数器这个程序统计一段文本中的单词数量:

#include #include #include

int count_words(const char *text) { int count = 0; int is_word = 0; // 标记是否在单词内

while (*text) { if (isspace(*text)) { is_word = 0; } else if (!is_word) { is_word = 1; count++; } text++; }

return count;}

int main() { char text[1000];

printf("请输入一段文本:\n"); fgets(text, sizeof(text), stdin);

// 移除结尾的换行符 text[strcspn(text, "\n")] = '\0';

int word_count = count_words(text);

printf("\n面试平台单词计数器\n"); printf("文本:%s\n", text); printf("单词数量:%d\n", word_count);

return 0;}简单的文本编辑器以下是一个简单的文本编辑器,可以添加、显示和搜索文本:

#include #include #include

#define MAX_LINES 100#define MAX_LINE_LENGTH 100

int main() { char text[MAX_LINES][MAX_LINE_LENGTH]; int line_count = 0; char command[10]; char line[MAX_LINE_LENGTH]; char search_term[50];

printf("效率工具简易文本编辑器\n"); printf("命令:add(添加行), show(显示全部), search(搜索), exit(退出)\n");

while (1) { printf("\n> "); scanf("%9s", command);

if (strcmp(command, "add") == 0) { if (line_count >= MAX_LINES) { printf("错误:已达到最大行数\n"); continue; }

printf("输入文本:"); getchar(); // 消耗上一个输入后的换行符 fgets(line, MAX_LINE_LENGTH, stdin);

// 移除换行符 line[strcspn(line, "\n")] = '\0';

strcpy(text[line_count], line); line_count++; printf("添加成功,当前共 %d 行\n", line_count); } else if (strcmp(command, "show") == 0) { printf("\n--- 文本内容 ---\n"); for (int i = 0; i < line_count; i++) { printf("%d: %s\n", i+1, text[i]); } printf("--- 结束 ---\n"); } else if (strcmp(command, "search") == 0) { printf("输入搜索关键词:"); scanf("%49s", search_term);

printf("\n--- 搜索结果 ---\n"); int found = 0; for (int i = 0; i < line_count; i++) { if (strstr(text[i], search_term) != NULL) { printf("%d: %s\n", i+1, text[i]); found++; } } printf("共找到 %d 处匹配\n", found); } else if (strcmp(command, "exit") == 0) { printf("谢谢使用!\n"); break; } else { printf("未知命令\n"); } }

return 0;}URL 解析器这个程序解析 URL 并提取其组成部分:

#include #include #include

void parse_url(const char *url, char *protocol, char *domain, char *path) { // 初始化为空字符串 protocol[0] = '\0'; domain[0] = '\0'; path[0] = '\0';

// 提取协议 const char *protocol_end = strstr(url, "://"); if (protocol_end) { size_t protocol_len = protocol_end - url; strncpy(protocol, url, protocol_len); protocol[protocol_len] = '\0';

// 移动指针到域名开始处 url = protocol_end + 3; }

// 提取域名 const char *domain_end = strchr(url, '/'); if (domain_end) { size_t domain_len = domain_end - url; strncpy(domain, url, domain_len); domain[domain_len] = '\0';

// 提取路径 strcpy(path, domain_end); } else { // 没有路径,整个剩余部分都是域名 strcpy(domain, url); strcpy(path, "/"); }}

int main() { char url[1000]; char protocol[20]; char domain[100]; char path[500];

printf("学习平台 URL 解析器\n"); printf("请输入一个 URL:"); fgets(url, sizeof(url), stdin);

// 移除换行符 url[strcspn(url, "\n")] = '\0';

// 解析 URL parse_url(url, protocol, domain, path);

// 显示结果 printf("\n解析结果:\n"); printf("协议:%s\n", protocol); printf("域名:%s\n", domain); printf("路径:%s\n", path);

return 0;}简单的密码生成器这个程序生成随机密码:

#include #include #include #include

void generate_password(char *password, int length) { const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+"; int charset_size = strlen(charset);

for (int i = 0; i < length; i++) { int index = rand() % charset_size; password[i] = charset[index]; }

password[length] = '\0'; // 添加结尾的空字符}

int main() { int length; char password[100];

// 初始化随机数生成器 srand(time(NULL));

printf("简历平台密码生成器\n"); printf("请输入密码长度:"); scanf("%d", &length);

if (length <= 0 || length > 99) { printf("无效的长度,使用默认长度 12\n"); length = 12; }

generate_password(password, length);

printf("生成的密码:%s\n", password);

return 0;}文本数据分析器这个程序分析文本数据并提供统计信息:

#include #include #include

void analyze_text(const char *text, int *chars, int *words, int *sentences, int *uppercase, int *lowercase, int *digits, int *spaces) { *chars = *words = *sentences = *uppercase = *lowercase = *digits = *spaces = 0; int is_word = 0;

while (*text) { (*chars)++;

if (isupper(*text)) (*uppercase)++; if (islower(*text)) (*lowercase)++; if (isdigit(*text)) (*digits)++; if (isspace(*text)) { (*spaces)++; is_word = 0; } else if (!is_word) { is_word = 1; (*words)++; }

if (*text == '.' || *text == '!' || *text == '?') { (*sentences)++; }

text++; }}

int main() { char text[1000]; int chars, words, sentences, uppercase, lowercase, digits, spaces;

printf("算法平台文本分析器\n"); printf("请输入一段文本:\n"); fgets(text, sizeof(text), stdin);

// 移除换行符 text[strcspn(text, "\n")] = '\0';

analyze_text(text, &chars, &words, &sentences, &uppercase, &lowercase, &digits, &spaces);

printf("\n分析结果:\n"); printf("字符总数:%d\n", chars); printf("单词数:%d\n", words); printf("句子数:%d\n", sentences); printf("大写字母:%d\n", uppercase); printf("小写字母:%d\n", lowercase); printf("数字:%d\n", digits); printf("空格:%d\n", spaces);

return 0;}练习题练习 1:编写一个函数,判断一个字符串是否是回文回文是指正序和倒序读都一样的字符串,例如 “level” 或 “上海自来水来自海上”。

#include #include

// 判断字符串是否为回文int is_palindrome(const char *str) { int len = strlen(str); const char *start = str; const char *end = str + len - 1;

while (start < end) { if (*start != *end) { return 0; // 不是回文 } start++; end--; }

return 1; // 是回文}

int main() { char text[100];

printf("请输入一个字符串:"); fgets(text, sizeof(text), stdin);

// 移除换行符 text[strcspn(text, "\n")] = '\0';

if (is_palindrome(text)) { printf("\"%s\" 是回文\n", text); } else { printf("\"%s\" 不是回文\n", text); }

return 0;}练习 2:编写一个函数,将字符串中的所有小写字母转换为大写字母不使用 toupper 函数,自己实现转换逻辑。

#include #include

// 将字符串转换为大写void to_upper(char *str) { while (*str) { if (*str >= 'a' && *str <= 'z') { *str = *str - 'a' + 'A'; } str++; }}

int main() { char text[100];

printf("请输入一个字符串:"); fgets(text, sizeof(text), stdin);

// 移除换行符 text[strcspn(text, "\n")] = '\0';

printf("原始字符串:%s\n", text);

to_upper(text);

printf("转换后的字符串:%s\n", text);

return 0;}练习 3:编写一个函数,从字符串中移除指定的字符#include #include

// 从字符串中移除所有指定的字符void remove_char(char *str, char c) { char *src = str; char *dst = str;

while (*src) { if (*src != c) { *dst = *src; dst++; } src++; }

*dst = '\0'; // 添加结尾的空字符}

int main() { char text[100]; char remove;

printf("请输入一个字符串:"); fgets(text, sizeof(text), stdin);

// 移除换行符 text[strcspn(text, "\n")] = '\0';

printf("请输入要移除的字符:"); scanf(" %c", &remove);

printf("原始字符串:%s\n", text);

remove_char(text, remove);

printf("移除字符 '%c' 后的字符串:%s\n", remove, text);

return 0;}这些练习题涵盖了不同难度的字符串处理任务,从简单的判断回文到更复杂的字符串操作。通过这些练习,可以加深对字符串处理的理解和掌握。

相关推荐

日产 GTR 在不同地区的售价有何区别
正规det365登录网站

日产 GTR 在不同地区的售价有何区别

🎯 12-17 💯 853
如何修改自己家的wifi密码
正规det365登录网站

如何修改自己家的wifi密码

🎯 01-03 💯 785
护卫神php套件怎么样、
365bet赌城投注

护卫神php套件怎么样、

🎯 02-07 💯 767