A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

C语言核心知识点相关总结(四)


九、文件操作;

数据I/O

#include <stdio.h>

1        fopen()函数:打开文件
函数原型:FILE *fopen(char restrict *filename, char restrict *mode);
           // restrict C99标准才引进的,属于类型修饰符,表示修饰的这块内存空间只能被这个指针引用和修改,除此之外别无他法。

参数:
                     filename: 需要打开的文件
                     mode: 文件打开方式

                     r       以只读的方式打开文件,前提是这个文件必须存在(只写 r 默认是文本文件)
                     r+      以可读可写的方式打开文件,前提是这个文件必须存在(默认是文本文件)
                     rb      以只读的方式打开一个二进制文件,前提是这个文件必须存在。
                     rb+    以可读可写的方式打开一个二进制文件,前提是这个文件必须存在。

                     w      以只写的方式打开文件,如果这个文件不存在,就创建这个文件;如果这个文件存在,则清空内容。
                     w+     以可读可写的方式打开文件,如果这个文件不存在,就创建这个文件;如果这个文件存在,则清空内容。
                     wb     以只写的方式打开一个二进制文件,如果这个文件不存在,就创建这个文件;如果这个文件存在,则清空内容。
                     wb+ 以可读可写的方式打开一个二进制文件,如果这个文件不存在,就创建这个文件;如果这个文件存在,则清空内容。

                     a       以追加的方式打开只写文件,如果这个文件不存在,就创建这个文件;如果这个文件存在,则在文件尾部追加内容。
                     a+      以追加的方式打开一个可读可写的文件,如果这个文件不存在,就创建这个文件;如果这个文件存在,则在文件尾部追加内容。
                     ab    以追加的方式打开一个二进制只写文件,如果这个文件不存在,就创建这个文件;如果这个文件存在,则在文件尾部追加内容。
                     ab+   以追加的方式打开一个二进制可读可写文件,如果这个文件不存在,就创建这个文件;如果这个文件存在,则在文件尾部追加内容。


                     r(read): ;
                     w(write):;
                     a(append):追加;
                     +(plus):读或写,主要是配合rwa使用;
                     t(text):文本文件;
                     b(binary):二进制文件

返回值:如果文件顺利打开,则返回值是指向这个文件流的文件指针,
如果文件打开失败,返回 NULL (void*)0

一般来说,文件打开失败会做一个文件指针错误判断
                     FILE *fp = fopen("c:\\code\\text.c","w+");
                     if(NULL == fp)
                     {
                                //code
                                //exit(-1);
                     }

2        fgetc(); fputc();
           1) fgetc()   文件字符读取函数
原型: int fgetc(FILE * stream);
参数: stream文件流
返回值:成功返回获取的字符ASCII码,失败返回 EOF(-1);
举例:
                     char ch = fgetc(fp);     // fp指向的文件流里接收文件流里的第一个字符


           2) fputc()   文件字符写入函数
原型: int fputc(int ch, FILE * stream);
参数:     ch :就是写入的字符,函数在执行的时候,会自动把 ch ASCII转换成一个 unsigned char 类型。
                                stream: 文件流
返回值:成功返回输出的字符,失败返回 EOF(-1);
举例:
                     fputc(ch, fp);                 //把字符ch写入到 fp 所指向的文件流里。

3        fgets(); fputs();
           1) fgets() 读取文件字符串函数
原型:char *fgets(char *str, int size, FILE* fp);
参数:     str : 保存从fp指向的文件流里读取的一行字符串。
                                size: 从文件流里读取的字符串不超过 size 个字符。( 一般会使用size - 1,留一个字符位置给 '\0')
                                fp     :文件指针
返回值:成功返回读取的字符串所在的内存首地址,失败返回 NULL(0);
举例:
                     char str[20] = { 0 };
                     fgets(str, 20 - 1, fp);             // fp指向的文件流的第一行里读取 19个字符,然后放到字符数组 str里。

           2) fputs() 写入文件字符串函数
原型:int fputs(char *str, FILE* fp);
参数:     str: 要写入到文件里字符串(不包括'\0')
                                fp: 文件指针,成功写入一个字符串后,文件指针会自动后移
返回值:成功为写入的字符个数,失败则返回 EOF(-1)
举例:
                     char str[20] = "Hello Kitty!";
                     fputs(str, fp);                                     // fp 指向的文件流里写入一个字符串 str,具体怎么写,看 mode 属性。

4        fprintf(); fscanf();
           1) fprintf() 将格式化后的数据写入到文件流里
原型: int fprintf(FILE *stream, char *format, argument...);
举例:     
                     int i = 10;
                     float f = 3.14;
                     char ch = 'C';
                     char str[10] = "haha";
                     fprintf(fp, "%d %f %c %s\n", i, f, ch,str); // 将各个数据按格式写入到文件流里

           2)       fscanf() 从文件流里获取数据格式化写入输入流里
原型: int fscanf(FILE *stream, char *format, argument... );
举例:
                     int i;
                     float f;
                     char ch;
                     char str[10];
                     fscanf(fp, "%d,%f", &i, &f);
                     // 如果不需要从文件里面写入字符串,那么就可以用逗号或者其他符号来分隔

                     fscanf(fp, "%s%c", str, &ch);
                     // 如果文件里需要写入字符串,那么字符串与其他数据之间只能用空格和回车来分隔


5        fread(); fwrite();          二进制文件读写函数
函数原型:
                                size_t fread(void *ptr, size_t size,size_t count, FILE* fp);
                                size_t fwrite(void *ptr, size_t size,size_t count, FILE* fp);
参数:     ptr: 是一个指针,对应 fread()来说,是从文件里读入的数据存放的地址;
对应 fwrite()来说,是写入到文件里的数据存放的地址。
                                          size: 每次要读写的字节数
                                          count : 读写的次数
                                          fp: 文件指针

返回值:成功读取/写入的字节数

举例:     
                                          char str[] = { 0 };
                                          fread(str, sizeof(char) *10, 1, fp);
                                          // 每次从fp指向的文件中读取10个字节大小,放入字符数组 str中,总共读1
                                          fwrite(str, sizeof(char) *10, 1, fp);
                                          // 每次从str里获取 10个字节大小,写入到 fp 指向的文件中,总共写1


6        fseek();    文件指针操作函数
函数原型: size_t fseek(FILE* fp, long offset, int whence);
参数:     fp 文件指针
                                          offset:  偏移量,基于起始点偏移了 offset 个字节
                                          whence : 起始点(三个):
                                                     SEEK_SET 0        文件开头位置
                                                     SEEK_CUR 1       当前位置
                                                     SEEK_END 2       文件结尾位置

举例
                                 fseek(fp, 0, SEEK_END);
                                 // 将文件指针指向文件结尾,并偏移了 0 个字节,也就是直接将文件指针指向文件结尾
                                 fseek(fp, -10, SEEK_CUR);
                                 // 将文件指针指向当前位置,并偏移了 -10 个字节,也就是将文件指针往前移动10个字节

7        ftell();                    文件指针操作函数
函数原型:  long ftell(FILE* fp);
参数:     fp  文件指针
返回值:返回文件指针当前位置,基于文件开头的偏移字节数,

举例: long len = ftell(fp);
                                          // 返回文件指针当前位置,基于文件开头的偏移字节数,保存到 len 里。

8        rewind();             文件指针操作函数
函数原型: void rewind(FILE* stream);
参数:     fp 文件指针

举例: rewind(fp);
                     // 将文件指针重新指向I/O流(文件流)的开头。

           stream > istream / ostream -> fstream -> sstream

           6\7\8 大例子

           FILE *fp = fopen("C:\\code\\a.txt","r+");
           fseek(fp, 0, SEEK_END);               //将文件指针指向文件结尾
           long len = ftell(fp);                 //获取文件指针位置,得到文件的大小(Byte)
           rewind(fp);                              //将文件指针重新指向文件开头

9        fflush();                清空数据流里的数据

函数原型:  void fflush(FILE* stream);
参数: stream 数据流

举例:
                                fflush(fp);            // 清空文件流
                                fflush(stdin);       // 清空输入流
                                fflush(stdout);    // 清空输出流


10 int stat(const char*path, struct stat *buf);
           // 自行补充


11 rename(); remove();

           rename(FILE* filename1, FILE* filename2);
                     rename("old_name.txt","new_name.txt");
           // old_name.txt 重命名为 new_name.txt

           remove(FILE* filename);
                     remove("C:\\code\\a.txt");
           // 绝对路径下的 a.txt 文件删除

12 feof();
原型: int feof(FILE* fp);
参数:     fp  文件指针
返回值:一旦文件指针指向文件结尾,就返回一个真值;否则返回非真值(0)

           1. 这个函数达到文件结尾的时候,返回的是一个真值,所以在做判断的时候要注意 !feof(fp)
           2. 这个函数必须对文件进行过一次读写操作才会生效,也就是说哪怕这个文件是空的,也必须读写一次,feof()才会返回真值。
文件结束是一个标识符,每次对文件读写都会修改这个标识符的位置,对文件读写一次,文件标识符才会被找到,feof()做出返回操作。

13 fclose();
原型: int flcose(FILE* fp);
参数:     fp 文件指针
返回值:如果成功释放,返回 0否则返回 EOF(-1);

           fclose(fp); 表示释放文件指针和相关的文件缓冲区,文件指针不再合法指向那块区域,但是不代表清空对应的区域。



UTF-8 编码格式下 一个汉字       3个字节
GBK 编码格式下    一个汉字        2个字节

// UTF-8 汉字逆置原理
#include <stdio.h>
#include <string.h>

int main(void)
{
           char str[] = "阿基米德";
           int len = strlen(str);

//       printf("%c%c\n", str[0], str[1]); // 打印""

           for (int i = len - 1; i > 0; i -= 3)       // UTF-8 编码下是3个字节,GBK下是2个字节
           {
                     printf("%c%c\n", str[i - 1], str);
           }


           return 0;
}

不同操作系统的行尾标志:
CR LF \ CR \ LF

CR '\r'          回车
LF '\n'          换行

DOSNT内核的Windows下,采用的是 回车+换行(CR LF '\r''\n') 来表示下一行的开始
Unix/Linux下,采用的是 换行(LF '\n') 来表示下一行的开始
Macintosh(OS X) ,采用的是 回车(CR '\r') 来表示下一行的开始



十、结构体

1. 结构体的字节对齐:
C语言里,结构体所占的内存是连续的,但是各个成员之间的地址不一定是连续的。所以就出现了"字节对齐".

结构体变量的大小,一定是其最大的数据类型的大小的整数倍,如果某个数据类型大小不够,就填充字节。
结构体变量的地址,一定和其第一个成员的地址是相同的。

1) 结构体字节对齐
#include <stdio.h>
#include <string.h>

struct Box
{
           // 首先检查结构体成员里最大的数据类型是 double, 8个字节,则
           int height;            //    系统判断 int 可以和相邻的 char name[10] 共同填充字节
           char name[10];          //    char name[10] 需要填充 double 的倍数,但是可以和相邻的 int 一起累加,再填充4个字节,对齐 double 2
           double width;     //     double 是所有成员里最大数据类型,满足double 1
           char type;                      //    char 会填充7个字节,对齐double 1
};

int main(void)
{
           struct Box box;
           box.height = 4;                                //高度
           strcpy(box.name, "Dropbox");  // 名称
           box.width = 5.5;                               //宽度
           box.type = 'C';                                   //类型

           printf("box = %p\n", &box);
           printf("box.height = %p\n", &box.height);
           printf("box.name = %p\n", box.name);
           printf("box.width = %p\n", &box.width);
           printf("box.type = %p\n", &box.type);

           printf("box = %d\n", sizeof(box));       // 16 + 8 + 8 = 24
           return 0;
}



2) 初识链表
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct Student
{
           char *name;                           //姓名
           int age;                           //年龄
           struct Student *next;           
           // next 是结构体成员,但是类型是 struct Student * 类型,用来指向某个 struct Student 的结构体变量的。
           // 结构体可以看做是一个自定义的数据类型,而且结构体可以嵌套,但是嵌套有条件:
           // 结构体只可以嵌套自身类型的结构体指针,但是绝对不能嵌套自身类型的结构体变量
           // 比如,不能嵌套 struct Student next; 这种
};

int main(void)
{
           struct Student stu, *stup;             //定义了一个结构体变量 stu 一个结构体指针变量 stup

           stu.name = (char *)malloc(10 * sizeof(char));     // 给姓名申请了一个10个字节的堆空间
           strcpy(stu.name, "damao");                                      // 拷贝字符串"damao" stu.name (注意,不能直接赋值,要用拷贝)
           stu.age = 18;                                     //今年 18岁了


           stup = (struct Student *)malloc(1 * sizeof(structStudent)); // stup 申请一个堆空间,用来保存两个指针(name,next)和一个int
           stup->name = (char *)malloc(10 * sizeof(char));                     // stup->name 申请一个堆空间,保存字符串
           strcpy(stup->name, "ermao");             // 拷贝字符串
           stup->age = 16;                                           //今年 16岁了

           stu.next = stup;                      //stu的成员next 指向了结构体变量 stup 的首地址,链表诞生
           stup->next = NULL;              //stup的成员 next 指向 NULL保证安全。

           free(stup->name);                //最后申请的堆 最先释放
           free(stup);                                          //继续释放
           free(stu.name);                               //最先申请的堆 最后释放

           return 0;              //程序正常结束
}


End...




0 个回复

您需要登录后才可以回帖 登录 | 加入黑马