其他章节
第三章 文件IO
第四章 文件和目录
第六章 系统数据文件和信息
第七章
第八章
第九章
第十章
提示:个人学习笔记,无参考价值
文章目录
第5章 标准I/O
1. fopen()
FILE * fopen(const char* path, const char* mode);
参数一:文件路径
参数二:打开模式
r 以读的方式打开,文件不存在时会报错。
r+ 以读写的方式打开,文件不存在时会报错。
w 以写的方式打开,文件存在则清空,不存在则创造
w+ 以读写的方式打开,文件存在则清空,不存在则创造
a 以追加写的方式打开,文件不存在则创造。写指针指向文件最后一个字符的下一个位置
a+ 以读和追加写的方式打开,文件不存在则创造。若读,则读指针指向文件的第一个字符;若写,则写指针指向文件最后一个字符的下一个位置
成功返回FILE指针,失败返回NULL并设置errno。
/usr/include/asm-generic/errno-base.h 文件中,对errno有宏定义。
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(){
FILE * myFile=fopen("./tem","r123");
if (myFile == NULL){
//打开文件失败,打印出错信息
printf("errno = %d", errno); //errno = 2
perror("fopen()"); //fopen(): No such file or directory
exit(1);
}
puts("Hello APUE");
exit(0);
}
上述代码打开的文件不存在,perror自动与errno关联,会打印出errno相对应的错误信息。
【注意】
fopen返回的指针,指向的数据块是存在堆中,而不栈区和静态区
文件的权限 = ox0666 & (umask取反)
下面代码测试一个进程最多能打开多少个文件
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(){
int count = 0;
FILE * fp = NULL;
while(true){
fp = fopen("./fopen.cpp","r123");
if (fp == NULL){
perror("fopen()");
break;
}
count ++;
}
printf("count = %d", count); //1048573 (2的20次方=1048576)
exit(0);
}
linux中每个进程默认情况下,最多可以打开1024个文件,最多有1024个文件描述符。
每个进程启动时默认打开0,1,2三个文件描述符(stdin,stdout,stderr)
2. fclose()
int fclose(FILE * fp);
成功返回0,失败返回EOF(宏)并设置errno。
3. fgetc() 与 fputc()
// reads the next character from stream and returns it as an un‐signed char cast to an int, or EOF on end of file or error.
int fgetc(FILE *stream);
//return the character written as an un‐signed char cast to an int or EOF on error.
int fputc(int c, FILE *stream);
读/写完一个字符后,读/写指针会后移一个字符。
案例一 拷贝文件
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char * argv[]){
if (argc < 3){
fprintf(stderr,"usage:%s srcFile destFile\n",argv[0]);
exit(1);
}
FILE * fps, * fpd;
fps = fopen(argv[1],"r");
if (fps == NULL){
perror("fps = fopen()");
exit(1);
}
fpd = fopen(argv[2],"w");
if (fpd == NULL){
fclose(fps);
perror("fpd = fopen()");
exit(1);
}
int ch = 0;
while (true){
ch = fgetc(fps);
if (ch == EOF){
break;
}
fputc(ch, fpd);
}
fclose(fpd);
fclose(fps);
exit(0);
}
案例二 计算文件字符数
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char * argv[]){
if (argc < 2){
fprintf(stderr,"usage:%s srcFile \n",argv[0]);
exit(1);
}
FILE * fps;
fps = fopen(argv[1],"r");
if (fps == NULL){
perror("fps = fopen()");
exit(1);
}
int count = 0;
while (fgetc(fps) != EOF){
count ++; //统计字符
}
fclose(fps);
printf("count = %d\n",count);
exit(0);
}
4. fgets() 与 fputs()
//returns pointer s on success, and NULL on error
char *fgets(char *s, int size, FILE *stream);
//return a nonnegative number on success, or EOF on error.
int fputs(const char *s, FILE *stream);
fgets两种正常结束情况:fgets(buf, size, stream);
1.读取了size-1个字节,第size个字节为'\0'
2.读取到了'\n',假设size=100,要读取的文件内容为ab,则buf的内容为 a b '\n' '\0'
特殊情况:要读取文件的内容为:abcd,size的值为5。则文件会被读两次,
第一次: a b c d '\0'
第二次:'\n' '\0'
案例 复制文件
#include <stdlib.h>
#include <stdio.h>
#define BUFSIZE 1024
int main(int argc, char * argv[]){
if (argc < 3){
fprintf(stderr,"usage:%s srcFile destFile\n",argv[0]);
exit(1);
}
FILE * fps, * fpd;
fps = fopen(argv[1],"r");
if (fps == NULL){
perror("fps = fopen()");
exit(1);
}
fpd = fopen(argv[2],"w");
if (fpd == NULL){
fclose(fps);
perror("fpd = fopen()");
exit(1);
}
char buf[BUFSIZE];
while (fgets(buf,BUFSIZE, fps)){
fputs(buf,fpd);
}
fclose(fpd);
fclose(fps);
exit(0);
}
5. frean() 与 fwrite()
#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
fread()参数解析:从stream中读取nmemb个对象,每个对象大小为size个字节,把读取的内存储存到ptr指的内存中
fwrite()参数解析:往stream中写nmemb个对象,每个对象大小为size个字节,写的内容由ptr指的内存块提供
fread()函数的返回值情况:
1.数据充足
2.只有5个字节
fread(buf, 1, 10, fp);
1 -> 读取10份数据,每一份1个字节
2 -> 读取5份数据,每一份 1个字节
fead(buf, 10, 1, fp);
1 -> 读取1份数据,这份10个字节
2 -> 读取了0份数据!!!
案例 复制文件
#include <stdlib.h>
#include <stdio.h>
#define BUFSIZE 1024
int main(int argc, char * argv[]){
if (argc < 3){
fprintf(stderr,"usage:%s srcFile destFile\n",argv[0]);
exit(1);
}
FILE * fps, * fpd;
fps = fopen(argv[1],"r");
if (fps == NULL){
perror("fps = fopen()");
exit(1);
}
fpd = fopen(argv[2],"w");
if (fpd == NULL){
fclose(fps);
perror("fpd = fopen()");
exit(1);
}
int n = 0;
char buf[BUFSIZE];
while ((n = fread(buf, 1, BUFSIZE, fps)) > 0){
fwrite(buf, 1, n, fpd);
}
fclose(fpd);
fclose(fps);
exit(0);
}
6. printf族 与 scanf族
int fprintf(FILE *stream, const char *format, ...);
int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char *format, ...);
#include <stdio.h>
#include <stdlib.h>
int main(){
int year=2023,month=9,day=26;
printf("%d-%d-%d\n",year,month,day);//2023-9-26
char str[1024];
//把格式化输出的字符串赋值给str
sprintf(str,"%d-%d-%d",year,month,day);//2023-9-26
puts(str);
//把格式化输出的字符串,按指定的长度,赋值给str
snprintf(str,11,"%d-%d-%d",year,month,day);//2023-9-26
puts(str);
exit(0);
}
int scanf(const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *str, const char *format, ...);
#include <stdio.h>
#include <stdlib.h>
int main(){
int a;
scanf("%d",&a);
printf("scanf() : a = %d\n",a);
fscanf(stdin,"%d",&a);
printf("fscanf() : a = %d\n",a);
exit(0);
}
7. fseek() 与 ftell()
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
long ftell(FILE *stream);
void rewind(FILE *stream);
-
feesk
改变文件读写指针的位置。成功返回0,失败返回-1并设置errno参数一:要改变的文件流 参数二:偏移量 参数三: - SEEK_SET:文件开头 - SEEK_CUR:当前位置 - SEEK_END:文件结尾
-
ftell
返回文件当前读写指针的位置 -
rewind
将文件的读写指针设置在开头处。相当于fseek(fp, 0l, SEEK_SET);
案例 利用feesk和ftell函数计算文件大小
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char * argv[]){
if (argc < 2){
fprintf(stderr,"usage:%s srcFile \n",argv[0]);
exit(1);
}
FILE * fps;
fps = fopen(argv[1],"r");
if (fps == NULL){
perror("fps = fopen()");
exit(1);
}
fseek(fps, 0, SEEK_END);
long size = ftell(fps);
printf("file's size is %ld\n", size);
fclose(fps);
exit(0);
}
fseek() 与 ftell()的缺陷
假设long为4个字节,则offset的大小为 (-2的31次-1) ~(+2的31次-1),而有ftell返回的数值不可能是负的,所以ftell的返回值只能三0~(+2的31次-1)即0~2G-1,这样就限定的文件的大小只能小于(2G-1)B。
改善:fseeko() 与 ftello()
8. fseeko() 与 ftello()
//在一些体系结构, off_t 和 long 都是 32-bit , 但是加上了 #define _FILE_OFFSET_BITS 64 (必须在所有头文件之前添加) ,这将会把off_t变成64-bit
#include <stdio.h>
int fseeko(FILE *stream, off_t offset, int whence);
off_t ftello(FILE *stream);
off_t可以选择32位,也可以选择64位。
9. fflush()
刷新缓冲区
int fflush(FILE *stream);
参数三所要刷新的流。 如果NULL作为参数,将会刷新所有已经打开的输出流。
缓冲区
-行缓冲:换行的时候刷新,满了的时候刷新,还可以强制刷新(如标准输出)
-全缓冲:满了的时候刷新,还可以强制刷新(文件默认是这种模式)
-无缓冲:如stderr,报错立刻输出内容
案例 刷新输出缓冲区
#include <stdlib.h>
#include <stdio.h>
int main(){
printf("before while");
fflush(stdout);
while(1){}
printf("after while");
fflush(NULL);
exit(0);
}
如果没有fflush(stdout);
,则运行代码不会有任何输出,因为"before while"字符串没有填满输出缓存区,所以刷新一次才会输出。
10. getline
#include <stdio.h>
ssize_t getline(char **lineptr, size_t *n, FILE *stream);
函数原理:先malloc一块空间进行读,空间满了再用realloc重新取一块更大的空间进行读操作。
返回值:成功返回读到的字符个数,包含分隔符,但不包含’\0’; 失败返回-1.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[]){
if (argc != 2){
fprintf(stderr,"usage: %s filename\n",argv[0]);
exit(1);
}
FILE * fp = NULL;
fp = fopen(argv[1],"r");
if (fp == NULL){
perror("fopen()");
exit(1);
}
char * str = NULL; //传出参数,获得读到的数据
size_t len = 0; //传出参数,动态申请的空间的大小
while(true){
if ((getline(&str, &len, fp)) < 0){
break;
}
printf("str::len = %ld - len = %ld\n",strlen(str),len);
}
fclose(fp);
exit(0);
}
11. 临时文件
【关注的问题】
1.创建的文件,命名不冲突
2.创建的文件要及时销毁
char *tmpnam(char *s);
FILE *tmpfile(void);
tmpnam函数:返回一个可用的临时文件名字。但存在缺陷,若a进程获取到文件名,在使用fopen创建文件之前,若有另一个进程也调用temnam函数,恰巧获取了同样的名字。那么就会出现两个进程在使用同一个临时文件名创建文件,从而出现错误。
tmpfile函数:创造匿名的临时文件,该文件没有名字,函数返回FILE*指针。且当文件引用数为0时,系统会销毁该文件。所以就不会出现命名冲突和销毁不及时的情况。