2025年3月17日 星期一 甲辰(龙)年 月十六 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > C语言

C语言从入门到精通保姆级教程(十二)

时间:07-25来源:作者:点击数:29

链表删除

  • void deleteNodeList(Node *head, Node *find){
  •     while(head->next != find){
  •         head = head->next;
  •     }
  •     head->next = find->next;
  •     free(find);
  • }

作业

  • 给链表排序
  • /**
  •  * @brief bubbleSort 对链表进行排序
  •  * @param head 链表头指针
  •  */
  • void bubbleSort(Node *head){
  •     // 1.计算链表长度
  •     int len = listLength(head);
  •     // 2.定义变量记录前后节点
  •     Node *cur = NULL;
  •    // 3.相邻元素进行比较, 进行冒泡排序
  •     for(int i = 0; i < len - 1; i++){
  •         cur = head->next;
  •         for(int j = 0; j < len - 1 - i; j++){
  •             printf("%i, %i\n", cur->data, cur->next->data);
  •             if((cur->data) > (cur->next->data)){
  •                 int temp = cur->data;
  •                 cur->data = cur->next->data;
  •                 cur->next->data = temp;
  •             }
  •             cur = cur->next;
  •         }
  •     }
  • }
  • /**
  •  * @brief sortList 对链表进行排序
  •  * @param head 链表头指针
  •  */
  • void sortList(Node *head){
  •     // 0.计算链表长度
  •     int len = listLength(head);
  •     // 1.定义变量保存前后两个节点
  •     Node *sh, *pre, *cur;
  •     for(int i = 0; i < len - 1; i ++){
  •         sh = head; // 头节点
  •         pre = sh->next; // 第一个节点
  •         cur = pre->next; // 第二个节点
  •         for(int j = 0; j < len - 1 - i; j++){
  •             if(pre->data > cur->data){
  •                 // 交换节点位置
  •                 sh->next = cur;
  •                 pre->next = cur->next;
  •                 cur->next = pre;
  •                 // 恢复节点名称
  •                 Node *temp = pre;
  •                 pre = cur;
  •                 cur = temp;
  •             }
  •             // 让所有节点往后移动
  •             sh = sh->next;
  •             pre = pre->next;
  •             cur = cur->next;
  •         }
  •     }
  • }
  • 链表反转
  • /**
  •  * @brief reverseList 反转链表
  •  * @param head 链表头指针
  •  */
  • void reverseList(Node *head){
  •     // 1.将链表一分为二
  •     Node *pre, *cur;
  •     pre = head->next;
  •     head->next = NULL;
  •     // 2.重新插入节点
  •     while(pre){
  •         cur = pre->next;
  •         pre->next = head->next;
  •         head->next = pre;
  •         pre = cur;
  •     }
  • }

文件基本概念

  • 文件流:
    C 语言把文件看作是一个字符的序列,即文件是由一个一个字符组成的字符流,因此 c 语言将文件也称之为文件流。
  • 文件分类
  • 文本文件
  • 以 ASCII 码格式存放,一个字节存放一个字符文本文件的每一个字节存放一个 ASCII 码,代表一个字符。这便于对字符的逐个处理,但占用存储空间 较多,而且要花费时间转换。
  • .c文件就是以文本文件形式存放的
  • 二进制文件
  • 以补码格式存放。二进制文件是把数据以二进制数的格式存放在文件中的,其占用存储空间较少。数据按其内存中的存储形式原样存放
  • .exe文件就是以二进制文件形式存放的

  • 文本文件和二进制文件示例
  • 下列代码暂时不要求看懂, 主要理解什么是文本文件什么是二进制文件
  • #include <stdio.h>
  • int main()
  • {
  •     /*
  •      * 以文本形式存储
  •      * 会将每个字符先转换为对应的ASCII,
  •      * 然后再将ASCII码的二进制存储到计算机中
  •      */
  •     int num = 666;
  •     FILE *fa = fopen("ascii.txt""w");
  •     fprintf(fa, "%d", num);
  •     fclose(fa);
  •     /*
  •      * 以二进制形式存储
  •      * 会将666的二进制直接存储到文件中
  •      */
  •     FILE *fb = fopen("bin.txt""w");
  •     fwrite(&num, 41, fb);
  •     fclose(fb);
  •     return 0;
  • }
  • 内存示意图
    图片
  • 通过文本工具打开示意图
    图片
  • 文本工具默认会按照ASCII码逐个直接解码文件, 由于文本文件存储的就是ASCII码, 所以可以正常解析显示, 由于二进制文件存储的不是ASCII码, 所以解析出来之后是乱码

文件的打开和关闭

  • FILE 结构体
  • FILE 结构体是对缓冲区和文件读写状态的记录者,所有对文件的操作,都是通过FILE 结构体完成的。
  •   struct _iobuf {
  •     char *_ptr;  //文件输入的下一个位置
  •     int _cnt;  //当前缓冲区的相对位置
  •     char *_base; //文件的起始位置)
  •     int _flag; //文件标志
  •     int _file;  //文件的有效性验证
  •     int _charbuf; //检查缓冲区状况,如果无缓冲区则不读取
  •     int _bufsiz; // 缓冲区大小
  •     char *_tmpfname; //临时文件名
  •   };
  •   typedef struct _iobuf FILE;

  • fileopen函数
函数声明 FILE * fopen ( const char * filename, const char * mode );
所在文件 stdio.h
函数功能 以 mode 的方式,打开一个 filename 命名的文件,返回一个指向该文件缓冲的 FILE 结构体指针。

|参数及返回解析 |参数| charfilaname :要打开,或是创建文件的路径。| |参数| charmode :打开文件的方式。| |返回值| FILE* 返回指向文件缓冲区的指针,该指针是后序操作文件的句柄。|

mode 处理方式 当文件不存在时 当文件存在时 向文件输入 从文件输出
r 读取 出错 打开文件 不能 可以
w 写入 建立新文件 覆盖原有文件 可以 不能
a 追加 建立新文件 在原有文件后追加 可以 不能
r+ 读取/写入 出错 打开文件 可以 可以
w+ 写入/读取 建立新文件 覆盖原有文件 可以 可以
a+ 读取/追加 建立新文件 在原有文件后追加 可以 可以

注意点:

  • Windows如果读写的是二进制文件,则还要加 b,比如 rb, r+b 等。unix/linux 不区分文本和二进制文件

  • fclose函数
函数声明 int fclose ( FILE * stream );
所在文件 stdio.h
函数功能 fclose()用来关闭先前 fopen()打开的文件.
函数功能 此动作会让缓冲区内的数据写入文件中, 并释放系统所提供的文件资源
参数及返回解析  
参数 FILE* stream :指向文件缓冲的指针。
返回值 int 成功返回 0 ,失败返回 EOF(-1)。
  • #include <stdio.h>
  • int main()
  • {
  •     FILE *fp = fopen("test.txt""w+");
  •     fclose(fp);
  •     return 0;
  • }

--

一次读写一个字符

  • 写入
函数声明 int fputc (int ch, FILE * stream );
所在文件 stdio.h
函数功能 将 ch 字符,写入文件。
参数及返回解析  
参数 FILE* stream :指向文件缓冲的指针。
参数 int : 需要写入的字符。
返回值 int 写入成功,返回写入成功字符,如果失败,返回 EOF。
  • #include <stdio.h>
  • int main()
  • {
  •     // 1.打开一个文件
  •     FILE *fp = fopen("test.txt""w+");
  •     // 2.往文件中写入内容
  •     for(char ch = 'a'; ch <= 'z'; ch++){
  •         // 一次写入一个字符
  •         char res = fputc(ch, fp);
  •         printf("res = %c\n", res);
  •     }
  •     // 3.关闭打开的文件
  •     fclose(fp);
  •     return 0;
  • }
  • 读取
函数声明 int fgetc ( FILE * stream );
所在文件 stdio.h
函数功能 从文件流中读取一个字符并返回。
参数及返回解析  
参数 FILE* stream :指向文件缓冲的指针。
返回值 int 正常,返回读取的字符;读到文件尾或出错时,为 EOF。
  • #include <stdio.h>
  • int main()
  • {
  •     // 1.打开一个文件
  •     FILE *fp = fopen("test.txt""r+");
  •     // 2.从文件中读取内容
  •     char res = EOF;
  •     while((res = fgetc(fp)) != EOF){
  •         printf("res = %c\n", res);
  •     }
  •     // 3.关闭打开的文件
  •     fclose(fp);
  •     return 0;
  • }
  • 判断文件末尾
    • feof函数
函数声明 int feof( FILE * stream );
所在文件 stdio.h
函数功能 判断文件是否读到文件结尾
参数及返回解析  
参数 FILE* stream :指向文件缓冲的指针。
返回值 int 0 未读到文件结尾,非零 读到文件结尾。
  • #include <stdio.h>
  • int main()
  • {
  •     // 1.打开一个文件
  •     FILE *fp = fopen("test.txt""r+");
  •     // 2.从文件中读取内容
  •     char res = EOF;
  •     // 注意: 由于只有先读了才会修改标志位,
  •     // 所以通过feof判断是否到达文件末尾, 一定要先读再判断, 不能先判断再读
  •     while((res = fgetc(fp)) && (!feof(fp))){
  •         printf("res = %c\n", res);
  •     }
  •     // 3.关闭打开的文件
  •     fclose(fp);
  •     return 0;
  • }
  • 注意点:
  • feof 这个函数,是去读标志位判断文件是否结束的。
  • 而标志位只有读完了才会被修改, 所以如果先判断再读标志位会出现多打一次的的现象
  • 所以企业开发中使用feof函数一定要先读后判断, 而不能先判断后读
  • 作业
  • 实现文件的简单加密和解密
  • #include <stdio.h>
  • #include <string.h>
  • void encode(char *name, char *newName, int code);
  • void decode(char *name, char *newName, int code);
  • int main()
  • {
  •     encode("main.c""encode.c"666);
  •     decode("encode.c""decode.c"666);
  •     return 0;
  • }
  • /**
  •  * @brief encode 加密文件
  •  * @param name 需要加密的文件名称
  •  * @param newName 加密之后的文件名称
  •  * @param code 秘钥
  •  */
  • void encode(char *name, char *newName, int code){
  •     FILE *fw = fopen(newName, "w+");
  •     FILE *fr = fopen(name, "r+");
  •     char ch = EOF;
  •     while((ch = fgetc(fr)) && (!feof(fr))){
  •         fputc(ch ^ code, fw);
  •     }
  •     fclose(fw);
  •     fclose(fr);
  • }
  • /**
  •  * @brief encode 解密文件
  •  * @param name 需要解密的文件名称
  •  * @param newName 解密之后的文件名称
  •  * @param code 秘钥
  •  */
  • void decode(char *name, char *newName, int code){
  •     FILE *fw = fopen(newName, "w+");
  •     FILE *fr = fopen(name, "r+");
  •     char ch = EOF;
  •     while((ch = fgetc(fr)) && (!feof(fr))){
  •         fputc(ch ^ code, fw);
  •     }
  •     fclose(fw);
  •     fclose(fr);
  • }

一次读写一行字符

  • 什么是行
  • 行是文本编辑器中的概念,文件流中就是一个字符。这个在不同的平台是有差异的。window 平台 '\r\n',linux 平台是'\n'
  • 平台差异
  • windows 平台在写入'\n'是会体现为'\r\n',linux 平台在写入'\n'时会体现为'\n'。windows 平台在读入'\r\n'时,体现为一个字符'\n',linux 平台在读入'\n'时,体现为一个字符'\n'
  • linux 读 windows 中的换行,则会多读一个字符,windows 读 linux 中的换行,则没有问题
  • #include <stdio.h>
  • int main()
  • {
  •     FILE *fw = fopen("test.txt""w+");
  •     fputc('a'fw);
  •     fputc('\n'fw);
  •     fputc('b'fw);
  •     fclose(fw);
  •     return 0;
  • }
图片

  • 写入一行
函数声明 int fputs(char *str,FILE *fp)
所在文件 stdio.h
函数功能 把 str 指向的字符串写入 fp 指向的文件中。
参数及返回解析  
参数 char * str : 表示指向的字符串的指针。
参数 FILE *fp : 指向文件流结构的指针。
返回值 int 正常,返 0;出错返 EOF。
  • #include <stdio.h>
  • int main()
  • {
  •     FILE *fw = fopen("test.txt""w+");
  •     // 注意: fputs不会自动添加\n
  •     fputs("lnj\n", fw);
  •     fputs("it666\n", fw);
  •     fclose(fw);
  •     return 0;
  • }
  • 遇到\0自动终止写入
  • #include <stdio.h>
  • int main()
  • {
  •     FILE *fp = fopen("test.txt""w+");
  •     // 注意: fputs写入时遇到\0就会自动终止写入
  •     fputs("lnj\0it666\n", fp);
  •     fclose(fp);
  •     return 0;
  • }

  • 读取一行
函数声明 char *fgets(char *str,int length,FILE *fp)
所在文件 stdio.h
函数功能 从 fp 所指向的文件中,至多读 length-1 个字符,送入字符数组 str 中, 如果在读入 length-1 个字符结束前遇\n 或 EOF,读入即结束,字符串读入后在最后加一个‘\0’字符。
参数及返回解析  
参数 char * str :指向需要读入数据的缓冲区。
参数 int length :每一次读数字符的字数。
参数 FILE* fp :文件流指针。
返回值 char * 正常,返 str 指针;出错或遇到文件结尾 返空指针 NULL。
  • 最多只能读取N-1个字符
  • #include <stdio.h>
  • int main()
  • {
  •     FILE *fp = fopen("test.txt""w+");
  •     // 注意: fputs不会自动添加\n
  •     fputs("it666\n", fp);
  •     // 将FILE结构体中的读写指针重新移动到最前面
  •     // 注意: FILE结构体中读写指针每读或写一个字符后都会往后移动
  •     rewind(fp);
  •     char str[1024];
  •     // 从fp中读取4个字符, 存入到str中
  •     // 最多只能读取N-1个字符, 会在最后自动添加\0
  •     fgets(str, 4, fp);
  •     printf("str = %s", str); // it6
  •     fclose(fp);
  •     return 0;
  • }
  • 遇到\n自动结束
  • #include <stdio.h>
  • int main()
  • {
  •     FILE *fp = fopen("test.txt""w+");
  •     // 注意: fputs不会自动添加\n
  •     fputs("lnj\n", fp);
  •     fputs("it666\n", fp);
  •     // 将FILE结构体中的读写指针重新移动到最前面
  •     // 注意: FILE结构体中读写指针每读或写一个字符后都会往后移动
  •     rewind(fp);
  •     char str[1024];
  •     // 从fp中读取1024个字符, 存入到str中
  •     // 但是读到第4个就是\n了, 函数会自动停止读取
  •     // 注意点: \n会被读取进来
  •     fgets(str, 1024, fp);
  •     printf("str = %s", str); // lnj
  •     fclose(fp);
  •     return 0;
  • }
  • 读取到EOF自动结束
  • #include <stdio.h>
  • int main()
  • {
  •     FILE *fp = fopen("test.txt""w+");
  •     // 注意: fputs不会自动添加\n
  •     fputs("lnj\n", fp);
  •     fputs("it666", fp);
  •     // 将FILE结构体中的读写指针重新移动到最前面
  •     // 注意: FILE结构体中读写指针每读或写一个字符后都会往后移动
  •     rewind(fp);
  •     char str[1024];
  •     // 每次从fp中读取1024个字符, 存入到str中
  •     // 读取到文件末尾自动结束
  •     while(fgets(str, 1024, fp)){
  •         printf("str = %s", str);
  •     }
  •     fclose(fp);
  •     return 0;
  • }
  • 注意点:
    • 企业开发中能不用feof函数就不用feof函数
    • 如果最后一行,没有行‘\n’的话则少读一行
  • #include <stdio.h>
  • int main()
  • {
  •     FILE *fp = fopen("test.txt""w+");
  •     // 注意: fputs不会自动添加\n
  •     fputs("12345678910\n", fp);
  •     fputs("12345678910\n", fp);
  •     fputs("12345678910", fp);
  •     // 将FILE结构体中的读写指针重新移动到最前面
  •     // 注意: FILE结构体中读写指针每读或写一个字符后都会往后移动
  •     rewind(fp);
  •     char str[1024];
  •     // 每次从fp中读取1024个字符, 存入到str中
  •     // 读取到文件末尾自动结束
  •     while(fgets(str, 1024, fp) && !feof(fp)){
  •         printf("str = %s", str);
  •     }
  •     fclose(fp);
  •     return 0;
  • }
  • 作业:
    • 利用fgets(str, 5, fp)读取下列文本会读取多少次?
  • 12345678910
  • 12345
  • 123
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐