关于流,内存数据读写铺垫的复习
- 打开一个文件 -> 内存里面就会创建一个对应关联的文件信息区 -> 文件信息区其实就是一个FILE类型的结构体 -> 各个结构体成员记录了该文件的种种信息 -> 结构体(文件信息区)由FILE* 结构体指针来维护 -> 有了指针,一切都好说了
- 输入输出/读写都是站在内存/程序的角度
- 任何一个C语言程序运行的时候,默认会打开三个流:stdin(标准输入), stdout(标准输出), stderr(标准错误)。stdin, stdout, stderr就是三个管理标准输入流,标准输出流,标注错误流的FILE*指针
- 这个流的类型就是FILE这样一个结构体。这个流其实就是由我们的FILEstream指针来管理的。流相当于是一个FILE类型的结构体,然后FILE相当于是维护流的一个指针
- 如果说内存数据要输出到文件里面,你要去看看针对文件的流有没有被打开, 如果说内存数据要输出到网络里面,你要去看看针对网络的流有没有被打开, 如果说内存数据要输出到软盘里面,你要去看看针对软盘的流有没有被打开…
- 因为我写程序他的数据不可能永远都是在内存当中,我有可能需要把这个数据放到文件当中,比如说需要放到我的终端设备当中,比如说要放到网络上面,当把数据放到这些外部输出设备的时候。对于程序员而言,他不一定懂各种细节,所以说在c语言当中就抽象出了流这么一个概念。就是说不管三七二十一,直接把数据放到流上面,然后具体这些数据是怎么传到各种各样的外部输出设备是自己会分装好的。除了在c语言当中,程序它默认会打开三个流之外,如果说根据特定的需求,需要把数据传到其他各种各样的外部输出设备的话,就需要打开针对各种各样外部输出设备的一个流,然后实际上就是通过FILE*指针(结构体指针)来操作流(FILE结构体)。
- 我们在操作文件的时候,首先要打开文件拥有这个流。打开文件就好比打开了(没有被C语言默认打开的)针对文件的流,fopen()返回的FILE*指针与维护流的指针是一个道理
利用输入输出流进行数据顺序读写(尤以文件为例)
文件里面没有整型等等,全部要么是字符,要么就是字符串,要么就是二进制,要么就是二进制信息解析成字符后的乱码
提醒一下:如果没有人为干预,文件指针都是自己自动动的
fputc() 一个字符输出
- 演示代码:
int main()
{
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen failed");
return;
}
fputc('U', pf);
fputc('S', pf);
fputc('B', pf);
fputc('U', stdout);
fputc('S', stdout);
fputc('A', stdout);
fclose(pf);
return 0;
}
fgetc() 一个字符输入
- 文件里面有多个字符的时候,我们用fgetc一个个去读取,此时定位文件位置的指针在fgetc每次读取完一次之后,就会后移一个字节往后走了一步指到下一个字符去了,因此fgetc一个个读取的话不需要顾虑太多,每次读取的时候只需要传参同一个FILE*结构体指针(事实上你其他的也传不了)
- 演示代码:
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen failed");
return;
}
printf("%c\n", fgetc(pf));
printf("%c\n", fgetc(pf));
printf("%c\n", fgetc(pf));
printf("%d\n", fgetc(pf));
fclose(pf);
return 0;
}
fputs() 一行字符串输出 + 一个\n的问题注意一下
- 由于fputs针对的是一行字符串的输出,如果说多次调用fputs的话,比如说把字符串给他写到文件里面去,那么这多个字符串之间并不会默认换行,如果说想要达到换行的这个效果,那么就必须在输出每行字符串的时候,在字符串的末尾加上转义字符换行符\n
- 演示代码:
int main()
{
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen failed");
return;
}
fputs("the United States Of American\n", pf);
fputs("the United States Of American\n", pf);
fclose(pf);
return 0;
}
fgets() 一行字符串输入 + \n三特性
- 中间这个参数就是指从外部输出设备的字符串输入到内存当中的字符的个数,因为是把字符串从外部输出设备输入到内存,然后它这个字符串结束符\0是默认会强制占掉一个空间,所以说比如说你想要往内存当中输进来六个字符的字符串,你那个第二个参数应该要设置成7。
- 然后这个函数它只能处理一行,比如说这一行只有三个字符,下一行有300个字符,然后你采用这个函数fgets,那个参数设置成50,那么这个函数执行完成之后,你读进来的也只有这一行的那三个字符+换行符 共4个字符的字符串而已。
- 在文件当中,每一行的字符串末尾都默认带有一个换行符。需要注意几点:1. 这个换行符是一个实体的存在,什么意思呢?主要影响就在于fgets的第二个参数,如果在统计的时候在一行当中延伸到了换行符\n,那么这个换行符也是算做一个字符,也需要参与计数。2. 拷贝文件一行字符串的时候换行符也是可能会被拷贝到内存字符串当中的,这时候打印的时候就会产生效果与影响。3. 当文件指针碰到文件字符串一行当中的换行符时,文件指针自己也会去换行。但无论如何要记住:fgets永远处理的都是一行字符串,文件指针换行后不会又继续去拷贝下一行内容。
- 演示代码:
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen failed");
return;
}
char arr1[20] = { 0 };
char arr2[20] = { 0 };
fgets(arr1, 16, pf);
fgets(arr2, 6, pf);
printf("arr1 : %s", arr1);
printf("arr2 : %s", arr2);
fclose(pf);
return 0;
}
fprintf() 格式化字符串输出 + 参照printf就可以了
- 如果用fprintf的话非常简单,就先正常按照printf去写,然后在括号最前面再加上一个参数(流指针),如:指向要写入文件的文件信息区的文件指针。这就是把格式化的信息写到文件里面去。
- 演示代码:
int main()
{
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
perror("fopen failed");
return;
}
fprintf(pf, "%c %d %s\n", 'Y', 2, "我真是醉了");
fprintf(pf, "%.3f %.1lf %s%c\n", 11.99954, 22.833, "无语了", 'Q');
fclose(pf);
return 0;
}
fscanf() 格式化字符串输入 + 文件指针换行问题不用考虑
- fscanf就是从文件里面把里面的信息拿到内存里面去。这个函数又与scanf十分相似,参数比scanf多了一个FILE*指针。
- 因此你先用scanf一如既往像以前那样正常写好,注意:scanf/fscanf的末尾几个参数都必须是地址形式,因为我是要把数据真真切切地读入到内存里面去,必须给我传地址!当然,数组名是首元素地址就不用&了。再在最前面加上参数:文件指针就OK了。
- 代码演示:
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen failed");
return;
}
char ch1 = 0;
char ch2 = 0;
int num = 0;
char arr1[20] = { 0 };
char arr2[20] = { 0 };
float f1 = 0;
double f2 = 0;
fscanf(pf, "%c %d %s\n", &ch1, &num, arr1);
fscanf(pf, "%f %lf %s %c\n", &f1, &f2, arr2, &ch2);
printf("ch1: %c\n",ch1);
printf("num: %d\n", num);
printf("arr1: %s\n", arr1);
printf("f1: %.2f\n", f1);
printf("f2: %.1lf\n", f2);
printf("arr2: %s\n", arr2);
printf("ch2: %c\n", ch2);
fclose(pf);
return 0;
}
fread() 与 fwrite() 二进制输入输出只能针对文件流
sprintf() 随意粘连任何已有东西+自己也可以添油加醋+组成一个新的字符串(类型很自由)
int main()
{
int num = 10;
char ch = 'A';
char* p1 = "我裂开了";
float f = 12.5345;
char* p2 = "哈哈哈哈哈";
char arr[100] = { 0 };
sprintf(arr, "%d%c%s %s %f 666666我的新字符串", num, ch, p1, p2, f);
printf("%s\n", arr);
return 0;
}
sscanf() 随意从已有字符串抠来一些东西 + 放到新的变量当中(类型很自由)
int main()
{
char arr[50] = "我哈哈哈哈哈 666 114514Q";
char arr1[50] = { 0 };
int num1 = 0;
int num2 = 0;
char ch = 0;
sscanf(arr, "%s %d %d %c", arr1, &num1, &num2, &ch);
printf("%s\n", arr1);
printf("%d\n", num1);
printf("%d\n", num2);
printf("%c\n", ch);
return 0;
}