C/C++(文件操作)

文件

读写磁盘文件

文件流

文件看作是一个字符的序列,即文件是由一个一个字符组成的字符流,因此C语言也将称之为文件流。当读写一个文件时,可以不必关心文件的格式或者结构。

文件类型

划分依据是逻辑关系

文本文件:以ASCII个是存放,一个字节存放一个字符。便于字符的逐个处理,但占用空间较多,需要花费时间转移。
二进制文件:以补码的编码格式存放,二进制文件是把数据以二进制数的格式存放在文件中,占用空间较小,数据按其内存中的存储形式鸳鸯存放。
数值写的时候有两种格式(文本,二进制),而文本写的时候文本文件和二进制文件是一样的。

文件缓冲

从内存中读取数据比从文件中读取数据要快的多。
对文件的读写需要用到open,read,write等系统底层函数,而用户进程每调用一次系统函数都要从永华态切换到内核态。执行完毕后再返回,这样花费时间成本。
window与linux是有区别的

int main() {
    printf("hello world");
    while(1);
    return 0;
}
//windows 下照样输出
//linux 下无法输出(printf("hello world
");上述换成此句即可输出,
有刷掉缓存的功能)

文件的打开与关闭

通过fopen打开一个文件,返回一个FILE *型指针。以后的所有对文件的操作也就是对FILE * 指针操作。

FILE结构体和FILE *指针

typedef struct {
    short level; /* 缓冲区满/空程度 */
    unsigned flags; /* 文件状态标志 */
    char fd; /* 文件描述符 */
    unsigned char hold; /* 若无缓冲区不读取字符 */
    short bsize; /*  缓冲区大小 */
    unsigned char *buffer; /*  数据传送缓冲区位置 */
    unsigned char *curp; /*  当前读写位置 */
    unsigned istemp; /* 临时文件指示 */
    short token; /* 用作无效检测 */
} FILE ; /* 结构体类型名 FILE */

在开始执行程序的时候,将自动打开 3 个文件和相关的流:标准输入流(stdin)、标准输出流(stdout)和标准错误(stderr),它们都是 FIEL*型的指针。流提供了文件和程序的
通信通道。

int main() {
    FILE * pf = fopen("data.txt","w");//w,通过写的方式打开文件data.txt,一打开就缓冲到内存中,以后操作pf(windos中交句柄)

    return 0;
}

fopen(padth,mode);

mode:
r:以只读的方式打开,如果文件不存在,则报错,文件不可写。
w:如果文件不存在,则创建,如果文件存在会覆盖文件,文件不可读。
a:(追加写)如果文件不存在,则创建,如果文件存在文件不可读。
r+ 可读/可写 文件不存在时报错,文件存在打开文件
w+ 可读/可写 文件不存在时创建,文件存在时会覆盖文件
a+ 读取/追加 文件不存在时创建,文件存在时在后面追加
如果都写的文件是二进制文件,则还需加b,eg:rb,rb+等,unix/linux不区分文本和二进制文件。

fclose()

fclose()用来关闭先前 fopen()打开的文件. 此动作会让缓冲区内的数据写入文件中, 并释放系统所提供的文件资源.

int main() {
    FILE * pf = fopen("data.txt","w");
    for(char ch = 'a';ch <= 'z';ch++) {
        fputc(ch,pf);//向pf中写入
    }
    fclose(pf);//主动刷新缓存,之前是没有写入的,如果不加等进程结束之后系统刷新。一般情况下要加,如果不加,在进程未结束之前还有突发情况(断电,硬件故障等)。而且还有释放缓存中的数据。

    return 0;
}

一次读写一个字符(文本操作)

fputc(int ch,FILE * stream)

将ch字符写入文件,stream:指向文件缓冲的指针,int:需要写入的字符。返回值int写入成功返回成功字符,失败返回EOF。

#defined F_PRINTF_ERROR(e)
do{
    if(e == NULL) {
        printf("open error");
        exit(-1);
    }
}while(0)
//宏的定义必须是一行

int main() {
    FILE *pf = fopen("xx.txt","w+");
    /*if(pf == NULL) {
        printf("open error!
");
        exit(-1);
    }*/ //判断每次要写,可以写成宏的形式
    F_PRINTF_ERROR(pf);
    putchar( fputc('a',pf) );//向pf中写入字符a.并且用putchar打印。
    fclose(pf);//释放
    return 0;
}

fgetc()读文件

读文件是需要判断是否结束,结束的标识EOF

#defined F_PRINTF_ERROR(e)
do{
    if(e == NULL) {
        printf("open error");
        exit(-1);
    }
}while(0)

int main() {
    FILE * pf = fopen("data.txt","r+");
    F_PRINTF_ERROR(pf);
    char ch = fgetc(pf);//结尾或者失败返回EOF
    putchar(ch);

    char ch = fgetc(pf);
    putchar(ch);//一次读取一个字节
    //假如文件中有4个字节,读取5次以上返回EOF
   
    //这时候可根据EOF条件进行循环
#if 0
    char ch;
    while( (ch = fgetc(pf)) != EOF) {//注意赋值运算和关系运算的优先级,赋值关系优先级倒数第二
        putchar(ch);
    }
#endif

    fclose(pf);
    return 0;
}

拷贝文件的实现

#defined F_PRINTF_ERROR(e)
do{
    if(e == NULL) {
        printf("open error");
        exit(-1);
    }
}while(0)

int main() {
    //先要都一个文件
    FILE * pfr = fopen("path...","r");
    F_PRINTF_ERROR(pfr);
    //写入文件
    FILE * pfw = fopen("path","w+");
    F_PRINTF_ERROR(pfw);
    //利用循环将读出来的文件进行写入
    while( (ch = fgetc(pfr)) != EOF ) {
        putchar(fputc(ch,pfw));
    }
    //最后释放
    fclose(pfr);
    fclose(pfw);

    return 0;
}

fputc(int ch,FILE * stream)

将ch字符写入文件,成功返回写入字符,失败返回EOF。
重点是判断结束条件是什么?通常的做法是一句返回值

feof的问题

检测到文件结尾返回1,0未结束。会导致多读一个字符,标志位检测之后所导致的。标志位检测之后所导致的。eg:读abcd,feof读的时候有一个标识flag = 0;每读一次指针向后推移,改变flag的状态,如果有值重新赋值为0,没有值flag=1,最后末尾添加-1。首先读一次,在循环输出,在读也可解决。

#defined F_PRINTF_ERROR(e)
do{
    if(e == NULL) {
        printf("open error");
        exit(-1);
    }
}while(0)

int main() {
    FILE * pf = fopen("data.txt","w");
    for(char ch = 'a';ch <= 'z';ch++) {
        fputc(ch,pf);//向pf中写入
    }
    char ch;
    while(!feof(ch)) {
        ch = fgetc(pf);
        printf("%x->%c
",ch,ch);
    }
    fclose(pf);

    return 0;
}

char ch;
ch = fgetc(pf);//首先获取第一个字符
while(!feof(pf)) {
    printf("%x->%c
",ch,ch);//获取的第一个字符输出
    ch = fgetc(ch);//紧接着获取
}

改进:

char ch;
while((ch = fgetc(pf)) && !feof(pf)) {
    printf("%x->%c
",ch,ch);
}

不建议使用

文件的加密与解密

文件加密:

#defined CODE 10
#defined F_PRINTF_ERROR(e)
do{
    if(e == NULL) {
        printf("open error");
        exit(-1);
    }
}while(0)

int main() {
    FILE * pfSrc = fopen("main.c","r+");
    F_PRINTF_ERROR(pfSrc);
    FILE * pfDec = fopen("mainIn.c","w+");
    F_PRINTF_ERROR(pfDec);

    char ch;
    while((ch = fgetc(pfSrc)) !=EOF) {
        ch += CODE;
        fputc(ch,pfDec);
    }
    fclose(pfSrc);
    fclose(pfDec);

    return 0;
}

文件的解密:

#defined CODE 10
#defined F_PRINTF_ERROR(e)
do{
    if(e == NULL) {
        printf("open error");
        exit(-1);
    }
}while(0)
//读的文件换一下
int main() {
    FILE * pfSrc = fopen("mainIn.c","r+");
    F_PRINTF_ERROR(pfSrc);
    FILE * pfDec = fopen("mainDein.c","w+");
    F_PRINTF_ERROR(pfDec);

    char ch;
    while((ch = fgetc(pfSrc)) !=EOF) {
        ch -= CODE;
        fputc(ch,pfDec);
    }
    fclose(pfSrc);
    fclose(pfDec);

    return 0;
}

打开要读的文件,同时打开要写的文件,对读到的文件进行加工操作后写入到要输出的文件中。
问题:加密的时候加密的数字太大会产生溢出

一次读一行字符(文本操作)

行:有回车就算行。linux下0a字符表示换行,windows下0d表示换行,如果把Windows下的文件拖到Linux下,则表示换行的文件无法解析。
同样在Windows下写的文件,在Linux中读的时候有差异。

fputs

写一行文件

#defined F_PRINTF_ERROR(e)
do{
    if(e == NULL) {
        printf("open error");
        exit(-1);
    }
}while(0)

int main() {
    FILE * pf = fopen("xxx.txt","w");
    F_PRINTF_ERROR(pf);

    fputs("aaa
aaaa
");
    fputs("bbbbbbb
");
    fputs("ccccccc
");//往里写遇到此行结束

    fclose(pf);
    return 0;
}

fgets

fgets(int buf,int size,LIFE * pf)按行读文件

再去读n-1个字符前遇到了 ,连同 一并读进去了,并在其后也添加了
在去读n-1既没有遇到 也没有遇到EOF,此时就读到了n-1个字符,并在其后添加了
在去读n-1既没有遇到 ,遇到EOF,并其后添加

#defined F_PRINTF_ERROR(e)
do{
    if(e == NULL) {
        printf("open error");
        exit(-1);
    }
}while(0)

int main() {
    FILE * pf = fopen("xxx.txt","r");
    F_PRINTF_ERROR(pf);

    char buf[1024];
    fgets(buf,1024,pf);//只读一行字符 aaa


    fclose(pf);
    return 0;
}

test:fgets( buf, 10, fp);一下文件fgets共执行了多少次?

1234567890abcdefg
1234567890
abcdefg(
) 有无
都会去读下一行的判断

第一次:1-9 
第二次:0-g 

第三次:1-9 
第四次:0   

第五次:a-g 
第六次:判断下一行

int main() {
    FILE * pf = fopen("aaa.txt","r+");
    char buf = [1024];
    while(fgetc(buf,1024,pf) != NULL) {//判断条件
        printf("%sx",buf);
    }
    fclose(pf);

    return 0;
}

读取的返回值:
当遇到 EOF的时候返回非控,在EOF以后,再去读的时候NULL

fgets的eof问题

int main() {
    FILE *pf = fopen("xxx.txt","w+");
    if(NULL == pf)
        exit(-1);
    fputs("aaaaaaaaa
",pf);
    fputs("bbbbbbbbb
",pf);
    fputs("ccccccccc
",pf);
    fputs("dddddddddd",pf);

    rewind(pf);//让指针重新指向第一个
    char buf[1024];
    /*
    while(fgets(buf,1024,pf) && !feof(pf)) {
        printf("%s",buf);
    }*/ //会多读了一行
    while(fgets(buf,1024,pf) != NULL) {//效果会更好,避免了一些坑
        printf("%s",buf);
    }//相对于更好
    return 0;
}

案例:从配置文件中读取用户名,和输入的用户名进行匹配。
从配置文件中读到用户名模拟出一个登录功能,从界面输入一个用户名,进行匹配对比。

xxx.ini文件

assassin  //注意有两个空格

main.c文件

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main() {
    FILE * pf = fopen("xxx.ini","r+");
    if(NULL == pf) {
        exit(-1);
    }
    char name[1024];
    scanf("%s",name);
    char buf[1024];
    fgets(buf,1024,pf);
    if(strcmp(name,buf) == 0) {
        printf("欢迎登录!
");
    }else {
        printf("登录失败!
");
    }

    return 0;
}

改进,在上述中如果不小心输入空格就会匹配不成功,过滤

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main() {
    FILE * pf = fopen("xxx.ini","r+");
    if(NULL == pf) {
        exit(-1);
    }
    char name[1024];
    scanf("%s",name);
    char buf[1024];
    fgets(buf,1024,pf);

    
    char *p = buf;
    while(*p) p++;//p到的位置
    p--;//p回到的前一个位置
    while(*p == "	") {
        *p = "";
        p--;
    }

    if(strcmp(name,buf) == 0) {
        printf("欢迎登录!
");
    }else {
        printf("登录失败!
");
    }

    return 0;
}

test:读配置文件,过滤掉以#开头的注释行和空行
注:空行(?)是以 开头的行,注释行是以#开头的行。

#include<stdio.h>

int main() {
    FILE *pf = fopen("smb.conf","r+");
    if(NULL == pf)
        exit(-1);

    char buf[1024];//一行全能读完
    FILE *pfbak = fopen("smb.conf.bak","w");//用来做备份的文件
    if(NULL == pfbak) {
        fclose(pf);//关闭读的文件
        exit(-1);
    }
    //配置文件有个不成文的规定每一行不能超过1024个字节
    while(fgets(buf,1024,pf)) {
        if(*buf == "#" || *buf == "
") {//匹配buf中的内容
            continue;
        }
        printf("%s",buf);
        fputs(buf,pfbak);
    }
    
    fclose(pf);
    fclose(pfbak);
    return 0;
}

test:文本等号以后就和:
atoi(),函数过滤函数。

一次读写一块字符(二进制操作)

文本文件以结束(写),要么以 结束(读),FOF(0xff),, 等是文本重要的标识,而二进制则往往是以块的形式写入或者读出。所有的二进制接口对于这些标识是不敏感的。

fwrite/fread

函数声明:

int fwrite(void *buffer,int num_bytes,int count,FILE *fp)
buffer: 代表即将要放数据的空间,
num_byte: 每次写的最小单元
count: 总共写的次数
fp: 写的目标文件
int fread(void *buffer,int num_bytes,int count,FILE *fp)

eg:

int main() {
    char buf[1024] = "a
bcdefg";
    FILE *pfa = fopen("xxx.txt","w+");
    fputs(buf,pfa);//实际文件中写入了a bc

    char readBuf = [1024];
    rewind(pfa);//让指向末尾的指针回到起始的地方
    fgets(readBuf,1024,pfa);//读写入的文件,读文件时结束位
位标识
    printf("%s",readBuf);//只输出a

    fclose(pfa);
    printf("
=========
");
    
    FILE *pfb = fopen("yyy.txt","wb+");//wb是二进制操作的标识
    fwrite((void*)buf,1,8,pfb);
    rewind(pfd);
    fread((void*)readBuf,1,8,pfd);
    for(int i = 0;i < 8;i++) {
        printf("%x->%c
",readBuf[i],readBuf[i]);
    }

    fclose(pfb);

    return 0;
}
/*
a
=======
61->a
a->

62->b
63->c
0->
64->d
65->e
66->f

*/

二进制和文本的读写

int main() {
    //以文本的形式写:
    FILE *pf = fopen("mm.txt","w+");
    int arr[10] = {0,1,0xffffffff,2,3,4,5,6,7,8};
    fputs((char*)arr,pf);//一个也写不进去,fputs是写文本的。
    fclose(pf);
    
    //以二进制的形式读写:
    FILE *pff = fopen("jj.txt","wb+");
    int arr1[10] = {0,1,0xffffffff,2,3,4,5,6,7,8};
    fwrite((void*)arr1,4,10,pff);
    //fgets不能读,因为fgets 遇到
,EOF(-1),文本结束就会结束。
    rewind(pff);
    int arr2[10];
    fread((void*)arr2,4,10,pff);//读文件
    for(int i = 0;i < 10;i++) {
        printf("%d
",arr2[i]);
    }
        
    fclose(pff);//能写入但是是乱码。
    
    return 0;
}

返回值陷阱

二进制没有 ,EOF,len-1作为都出的结束标识,fread依靠读出块多少来标识读结果和文件结束标志。以最小单元格式进行读,或者以写入的最小单元进行读。

int main() {
    char buf[1024] = "12345678";
    FILE *pf = fopen("xx.txt","w+");
    if(NULL == pf)
        exit(-1);
    //fwrite((void*)buf,1,8,pf);
    fwrite((void*)buf,8,1,pf);//这两种写法是没有区别的但是读的时候有差别

    //读到完整块的个数
    rewind(pf);
    int n;
    char read[10];
    n = fread((void*)read,1,8,pf);
    printf("n = %d
",n);//n = 8
    n = fread((void*)read,1,8,pf);
    printf("n = %d
",n);//n = 0
//读到的是整个块为单位,返回块的个数
/*
    n = fread((void*)read,8,1,pf);
    printf("n = %d
",n);//n = 1
    n = fread((void*)read,8,1,pf);
    printf("n = %d
",n);//n = 0
*/
    n = fread((void*)read,1,7,pf);
    printf("n = %d
",n);//n = 1
    n = fread((void*)read,1,7,pf);
    printf("n = %d
",n);//n = 0
    //
    n = fread((void*)read,1,3,pf);
    printf("n = %d
",n);//n = 3
    n = fread((void*)read,1,3,pf);
    printf("n = %d
",n);//n = 3
    n = fread((void*)read,1,3,pf);
    printf("n = %d
",n);//n = 2
    n = fread((void*)read,1,3,pf);
    printf("n = %d
",n);//n = 0
//一般情况下都是最小单位为1,这样便于遍历
    while((n = fread( (void*)read,1,3,pf) )>0) {
        for(int i = 0;i < 3;i++) {
            printf("%c",read[i]);
        }
    }
    //12345678

    fclose(pf);

    return 0;
}

一般来说读的单位是写入的最小单位决定的

int main() {
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    FILE *pf = fopen("xx.txt","w+");

    fwrite((void*)arr,4,10,pf);
    rewind(pf);
    int n = 10;
    while((n = fread( (void*)arr,4,1,pf)) > 0) {
        for(int i = 0;i < n;i++) {
            printf("%d
",arr[i]);
        }
    }
}

原文地址:https://www.cnblogs.com/intelwisd/p/8414832.html