黑马程序员技术交流社区
标题: 【笔记】C语言核心知识点相关总结(四) [打印本页]
作者: 倾心莫若初见 时间: 2017-2-28 14:39
标题: 【笔记】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):读或写,主要是配合r、w、a使用;
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' 换行
在DOS和NT内核的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...
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) |
黑马程序员IT技术论坛 X3.2 |