APUE笔记-第五章标准I/O

其他章节

第三章 文件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时,系统会销毁该文件。所以就不会出现命名冲突和销毁不及时的情况。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值