C 语言

10.C语言(文件操作)

2018-05-28  本文已影响18人  任振铭
1.FILE*文件指针讲解

FILE所有平台得名字都一样,FILE是一个结构体类型,里面得成员功能一样,不同平台成员得名字不一样,这个结构体可以在stdio.h中找到

typedef struct {
    //缓冲区“满”或者“空”得程度
    short level;   
    
    //文件状态标志
    unsigned flags;

    //文件描述符
    char fd;

    //如无缓冲区不读取字符
    unsigned char hold;

    //缓冲区大小
    short bsize;

    //数据缓冲区得位置
    unsigned char *buf;

    //指针,当前得指向
    unsigned ar;

    //临时文件,指示器
    unsigned istemp;

    //用于有效性得检查
    short token;
}FILE;

FILE *fp

1.fp指针,调用fopen(),在堆区分配空间,把地址返回给fp
2.fp指针不是指向文件,fp指针和文件关联,fp内部成员保存了文件得状态
3.操作fp指针,不能像一般指针类型那样操作,比如说*fp操作内存等等,必须通过文件库函数进行操作
4.通过库函数操作fp指针,对文件得任何操作,fp里面成员会相应得变化(系统自动完成)

2.文件分类

1)磁盘文件
指一组相关数据的有序集合,通常存储在外部介质上(磁盘),使用时才调入内存

2)设备文件
在操作系统中把每一个与主机相连得输入输出设备看作是一个文件,把对他们得输入输出等同于对磁盘文件得读和写

3)从用户或者操作系统使用的角度(逻辑上)把文件分为:
文本文件:基于字符编码的文件
二进制文件:基于值编码得文件

特殊的文件指针

由系统默认打开,用户无需定义即可直接使用
stdin:标准输入,默认为当前终端(键盘),我们使用得scanf,getchar函数默认从此终端获取数据
stdout:标准输出,默认当前终端(屏幕),我们使用得printf,puts函数默认输出信息到此终端
stderr:标准出错,默认为当前终端(屏幕),我们使用得perror函数默认输出信息到此终端

关闭标准函数指针后

//#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main() {

    //1.关闭标准输出函数指针,导致无法再打印到屏幕
    printf("aaaaaaaaaaa\n");
    fclose(stdout);
    printf("bbbbbbbbbb\n");

    //2.关闭标准错误函数指针,导致无法打印错误信息
    perror("ren");
    fclose(stderr);
    perror("zhen");

    //2.关闭标准输入函数指针,导致无法再输入
    int a = 10;
    printf("a = %d\n", a);

    printf("请输入a\n");
    fclose(stdin);
    scanf("%d", &a);
    printf("a = %d\n", a);
    system("pause");
}
#include <stdio.h>
#include <unistd.h>
int main() {

    //printf函数的内部实现,是往标准输出设备1写内容
    printf("before close..");
    //fclose(stdout);
    //1代表标准输出设备,关闭了,1就是空闲的数组
    close(1);

    //打印一下你会发现fd值就是1
    int fd = open("01.txt",O_WRONLY,0777);

    //printf函数的内部实现,往1写内容,但是1现在和01.txt关联
    //所以会把内容写入到01.txt中
    printf("fd = %d\n",fd);

    printf("after close bbbbbbbb");
    system("pause");
}
3.打开文件fopen

任何文件使用之前都要先打开

#include <stdio.h>
FILE *fopen(const char *filename,const char *mode);

参数:
filename:需要打开的文件名,根据需要加上路径
mode:打开文件的模式设置
返回值:文件指针,失败返回NULL

w:如果文件不存在,新建,如果文件存在,清空文件再打开
r:如果文件不存在,打开失败
a:如果文件不存在,新建,如果文件存在,将光标放在文件末尾并打开

//fopen不安全
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {

    FILE *fp = NULL;
    fp = fopen("./a.txt","r");
    if (fp == NULL) {
        perror("open failed:");
        return 0;
    }

    fclose(fp);
    fp = NULL;
    system("pause");
}
4.相对路径

相对路径相对于可执行程序,在不同平台各有各的规则
VS:
a.编译同时运行程序,相对路径相对于xxx.vcxproj(项目文件)所在的目录
b.如果直接运行程序,相对路径,相对于debug文件夹所在的目录

Qt:
a.编译同时运行程序,相对路径相对于debug所在的路径
b.如果直接运行程序,相对路径相对于可执行程序

5.写文件fputc() [按照字符写]

include <stdio.h>

int fputc(int ch,FILE *stream);
功能:将ch转换为unsigned char 后写入stream指定的文件中
参数:
ch:需要写入的文件字符
stream:文件指针
返回值:
成功:返回成功写入的字符
失败:返回-1

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {

    //1.打开文件
    FILE *fp = NULL;
    fp = fopen("D:/b.txt","w");
    if (fp == NULL) {
        perror("open failed:");
        return 0;
    }
    //2.写入文件,fputc一次只能写一个字符
    fputc('a', fp);
    fputc('b', fp);
    fputc('c', fp);
    fputc('d', fp);

    //3.关闭文件
    fclose(fp);
    fp = NULL;
    system("pause");
}
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {

    //1.打开文件
    FILE *fp = NULL;
    fp = fopen("D:/b.txt","w");
    if (fp == NULL) {
        perror("open failed:");
        return 0;
    }
    //2.写入文件,fputc一次只能写一个字符
    char ch = 'a';
    while (ch < 'z') {
        fputc(ch, fp);
        ch++;
    }

    char buf[] = "ABCDEF";
    int i = 0;
    //获取数组长度等价于 sizeof(buf)/sizeof(buf[0]),
    //  因为char只占一个字节,可以这样计算
    int n = strlen(buf);
    for (; i < n; i++) {
        fputc(buf[i], fp);
    }
    //3.关闭文件
    fclose(fp);
    fp = NULL;
    system("pause");
}
6.读文件fgetc() [按照字符读]

include <stdio.h>

int fgetc(FILE *stream);
功能:从stream指定的文件中读取一个字符
参数:
stream 文件指针
返回值:
成功返回读取到的字符
失败返回-1

注意,如果在写入完成后立即进行文件读取(没有重新以r的形式打开文件),是读不到内容的,如下这种方式不行

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {

    //1.打开文件
    FILE *fp = NULL;
    fp = fopen("D:/b.txt","w");
    if (fp == NULL) {
        perror("open failed:");
        return 0;
    }
    //2.写入文件,fputc一次只能写一个字符
    char ch = 'a';
    while (ch < 'z') {
        fputc(ch, fp);
        ch++;
    }

    //3.读取写入的字符
    char temp;
    while (1) {
        temp = fgetc(fp);
        printf("ch = %s\n", temp);
        if (temp == EOF) {
            break;
        }
    }

    char buf[] = "ABCDEF";
    int i = 0;
    //获取数组长度等价于 sizeof(buf)/sizeof(buf[0]),
    //  因为char只占一个字节,可以这样计算
    int n = strlen(buf);
    for (; i < n; i++) {
        fputc(buf[i], fp);
    }
    //3.关闭文件
    fclose(fp);
    fp = NULL;
    system("pause");
}

只有在写入完成后关闭这个文件,然后重新以只读的形式打开才能读取到

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {

    //1.打开文件
    FILE *fp = NULL;
    fp = fopen("D:/b.txt","w");
    if (fp == NULL) {
        perror("open failed:");
        return 0;
    }
    //2.写入文件,fputc一次只能写一个字符
    char ch = 'a';
    while (ch < 'z') {
        fputc(ch, fp);
        ch++;
    }

    
    char buf[] = "ABCDEF";
    int i = 0;
    //获取数组长度等价于 sizeof(buf)/sizeof(buf[0]),
    //  因为char只占一个字节,可以这样计算
    int n = strlen(buf);
    for (; i < n; i++) {
        fputc(buf[i], fp);
    }
    //3.关闭文件
    fclose(fp);
    fp = NULL;


    FILE *fp2 = NULL;
    fp2 = fopen("D:/b.txt", "r");
    if (fp2 == NULL) {
        perror("open failed:");
        return 0;
    }
    //3.读取写入的字符
    char temp;
    while (1) {
        temp = fgetc(fp2);
        printf("ch = %d\n", temp);
        if (temp == EOF) {
            break;
        }
    }

    system("pause");
}
7.判断文件结尾的方式

1).如果是文本文件,可以通过-1(EOF)判断文件是否结尾
2).如果是二进制文件,不能以-1判断文件结尾
3).feof()判断文件是否在结尾,任何文件都能判断

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {

    //1.打开文件
    FILE *fp = NULL;
    fp = fopen("D:/b.txt", "w");
    if (fp == NULL) {
        perror("open failed:");
        return 0;
    }
    //2.写入文件,fputc一次只能写一个字符
    fputc('a',fp);
    fputc('c', fp);
    fputc('d', fp);
    fputc('-1', fp);
    fputc('e', fp);
    fputc('-1', fp); 
    fputc('f', fp);

    //3.关闭文件
    fclose(fp);
    fp = NULL;


    FILE *fp2 = NULL;
    fp2 = fopen("D:/b.txt", "r");
    if (fp2 == NULL) {
        perror("open failed:");
        return 0;
    }
    //4.读取写入的字符
    char temp;
    while (1) {
        temp = fgetc(fp2);
        printf("ch = %d\n", temp);
        if (feof(fp2)) {
            break;
        }
    }
    system("pause");
}

feof(FILE *fp):如果到文件结尾,返回真
1)如果第一次没有对文件进行读操作,直接调用此函数永远返回真
2)此函数只有先读取文件再调用才有意义
3)调用此函数,文件状态中当前所在位置不会往后移动
4)必须读取后,才能判断是否结束,判断的是当前读取的字符

上边的程序稍作改动就可以验证这一点,去掉fputc的那行代码,你会发现,没有读取的操作,就用于不会到文件结尾

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {
    //1.打开文件
    FILE *fp = NULL;
    fp = fopen("D:/b.txt", "w");
    if (fp == NULL) {
        perror("open failed:");
        return 0;
    }
    //2.写入文件,fputc一次只能写一个字符
    fputc('a', fp);
    fputc('c', fp);
    fputc('d', fp);
    fputc('-1', fp);
    fputc('e', fp);
    fputc('-1', fp);
    fputc('f', fp);

    //3.关闭文件
    fclose(fp);
    fp = NULL;

    FILE *fp2 = NULL;
    fp2 = fopen("D:/b.txt", "r");
    if (fp2 == NULL) {
        perror("open failed:");
        return 0;
    }
    //4.读取写入的字符
    char temp;
    while (1) {
        //temp = fgetc(fp2);
        printf("文件没有到结尾\n");
        if (feof(fp2)) {
            break;
        }
    }

    system("pause");
}
8.使用fgetc完成简单的cat命令

将此代码编译成一个可执行文件,然后像cat 文件名 那样运行,就可以实现我们自己定义的cat命令,cat其实内部也是这样实现的

#include <stdio.h>
int main(int argc,char *argv[]) {

    //打开文件
    FILE *fp = fopen(argv[1], "r");

    //读取文件内容显示到屏幕
    char ch;
    while (1) {
        ch = fgetc(fp);
        if (feof(fp))
        {
            break;
        }
        printf("%c", ch);
    }

    //关闭文件
    fclose(fp);
}
9.使用fputc fgets完成简单的vi命令
#include <stdio.h>
int main(int argc,char *argv[]) {

    //以写的方式打开一个文件
    FILE *fp = fopen(argv[1], "w");
    //从键盘读取内容使用fgets()(按行读取,以换行符为结尾),
    //因为scanf遇到空格和回车会结束,gets遇到回车会结束,所以不适合这个场景
    //读取到字符后,一个一个写入文件
    char buf[1024];
    while (1) {
        //默认遇到换行符结束读取,换行符都放在buf
        fgets(buf, sizeof(buf), stdin);
        //如果用户输入内容为:wq,结束,保存文件
        //比较前3个字符是否相同
        if (strncmp(buf,":wq",3))
        {
            break;
        }
        int i = 0;
        while (buf[i] != '\0') {
            fputc(buf[i], fp);
            i++;
        }
    }

    fclose(fp);
    
}
10.fputs的使用 [按照行写]
#include <stdio.h>
int main() {
    FILE *fp = fopen("ren.txt", "w");
    //指针数组,指针组成的数组,它的每个元素都是指针
    char *p[] = {"hello\n","world\n","beauty\n"};
    int i = 0;
    int n = sizeof(p) / sizeof(p[0]);
    for (i = 0; i < n; i++) {
        fputs(p[i], fp);
    }
    fclose(fp);
}
11.fgets的使用 [按照行读]

可以看到打印结果中最后一个字符串打印了两次,为什么会这样呢,其实这时feof函数的问题,feof函数必须先读取然后才能判断是否到达末尾。也就是说,都是先读取一次,再判断一次,当读取到最后一个的时候,读取+判断,还没有到达末尾,然后再次读取+判断,而此时由于没有内容可以读取了,但是文件缓冲区中还有一个保存的上次读取到的内容,所以会把上次的东西再打印一次,导致beauty打印了两次,所以最好是将结尾的判断放在打印的前边,避免重复打印

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {
    FILE *fp = fopen("ren.txt", "w");
    //指针数组,指针组成的数组,它的每个元素都是指针
    char *p[] = {"hello\n","world\n","beauty\n"};
    int i = 0;
    int n = sizeof(p) / sizeof(p[0]);
    for (i = 0; i < n; i++) {
        fputs(p[i], fp);
    }
    fclose(fp);

    FILE *read = fopen("ren.txt", "r");
    char buf[100];
    //1.从fp关联的文件读取内容,放到buf中,一次最大读取sizeof(buf)-1
    //2.遇到换行符,文件到达结尾,结束本地读取
    while (1) {
        fgets(buf, sizeof(buf), fp);
        printf("buf = %s", buf);
        if (feof(read)) {
            break;
        }
    }
    fclose(read);
    system("pause");
}

打印结果
buf = hello
buf = world
buf = beauty
buf = beauty
请按任意键继续. . .

正确的读取方式

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {
    FILE *fp = fopen("ren.txt", "w");
    //指针数组,指针组成的数组,它的每个元素都是指针
    char *p[] = {"hello\n","world\n","beauty\n"};
    int i = 0;
    int n = sizeof(p) / sizeof(p[0]);
    for (i = 0; i < n; i++) {
        fputs(p[i], fp);
    }
    fclose(fp);

    FILE *read = fopen("ren.txt", "r");
    char buf[100];
    //1.从fp关联的文件读取内容,放到buf中,一次最大读取sizeof(buf)-1
    //2.遇到换行符,文件到达结尾,结束本地读取
    while (1) {
        fgets(buf, sizeof(buf), fp);
        if (feof(read)) {
            break;
        }
        printf("buf = %s", buf);
        
    }
    fclose(read);
    system("pause");
}
打印
buf = hello
buf = world
buf = beauty
请按任意键继续. . .

12.feof()判断文件结尾标准写法

这样的写法既可以保证不会重复打印最后一个字符串,也可以保证在最后一个字符串不管是不是存在换行符的情况下,都能正确打印

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

void write_file() {
    FILE *fp = fopen("1.txt","w");

    fputs("10+10 = \n", fp);
    fputs("10-10 = \n", fp);
    fputs("10*10 = \n", fp);
    fputs("10/10 = \n", fp);
    fputs("10+5 = \n", fp);
    fputs("10-5 = \n", fp);
    fputs("10*3 = \n", fp);

    fclose(fp);
}

void read_file() {
    FILE *fp = fopen("1.txt", "r");

    char buf[1024];
    while (1) {
        //fgets在文件中读取一行放在buf数组中,为了防止读取到文件末尾
        //读取到空时再次打印buf缓冲区中上一次读到的内容,每次读取之前
        //先清空一次
        //sizeof(buf)数组的大小 1024
        memset(buf, 0, sizeof(buf));
        fgets(buf, sizeof(buf), fp);

        //直接这样打印,导致一个问题,读取到结尾后仍然会在最后打印一个kong,
        //所以我们加一层判断,如果buf为空的时候就不再打印
        //printf("buf = %s\n", buf);
        if (strlen(buf) > 0) {
            printf("buf = %s\n", buf);
        }
        if (feof(fp)) {
            break;
        }
    }
}

int main() {
    write_file();
    read_file();
    system("pause");
}

打印
buf = 10+10 =

buf = 10-10 =

buf = 10*10 =

buf = 10/10 =

buf = 10+5 =

buf = 10-5 =

buf = 10*3 =

请按任意键继续. . .

13.提取文件中运算并得出结果重新写入文件

这段代码首先将一些四则运算以“a运算符b=“的规则写入到一个文件中,我们要做的就是把这些运算得出结果并重新写入这个文件,具体的思路就是使用fgets一行一行的从文件中读取内容,读取出来之后按照我们设定的规则“%d%c%d=\n”从每行中提取其中参与计算的ab两个值和运算符,这样我们就可以得到运算结果,有了运算结果然后再将它们格式化成一个字符串,一个一个的将这些包含运算结果的字符串重新写入文件中即可完成。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

void write_file() {
    FILE *fp = fopen("1.txt","w");

    fputs("10+10 = \n", fp);
    fputs("10-10 = \n", fp);
    fputs("10*10 = \n", fp);
    fputs("10/10 = \n", fp);
    fputs("10+5 = \n", fp);
    fputs("10-5 = \n", fp);
    fputs("10*3 = \n", fp);

    fclose(fp);
}

int calc(int a, int b, char ch) {
    switch (ch)
    {
    case '+':
        return a + b;
    case '-':
        return a - b;
    case '*':
        return a*b;
    case '/':
        return a / b;
    default:
        return 0;
    }
    return 0;
}

void read_file() {
    FILE *fp = fopen("1.txt", "r");

    char buf[1024];
    char temp[1024 * 4] = {0};
    while (1) {
        //fgets在文件中读取一行放在buf数组中,为了防止读取到文件末尾
        //读取到空时再次打印buf缓冲区中上一次读到的内容,每次读取之前
        //先清空一次
        //sizeof(buf)数组的大小 1024
        memset(buf, 0, sizeof(buf));
        fgets(buf, sizeof(buf), fp);

        //直接这样打印,导致一个问题,读取到结尾后仍然会在最后打印一个空,
        //所以我们加一层判断,如果buf为空的时候就不再打印
        //printf("buf = %s\n", buf);
        if (strlen(buf) > 0) {
            //printf("buf = %s\n", buf);
            int a, b;
            char ch;
            sscanf(buf, "%d%c%d=\n", &a,  &ch, &b);
            //printf("%d%c%d=%d\n", a, ch, b,calc(a,b,ch));
            sprintf(buf,"%d%c%d=%d\n", a, ch, b, calc(a, b, ch));
            strncat(temp,buf,sizeof(buf));
            printf(temp);
            printf("---------------------\n");
        }
        if (feof(fp)) {
            break;
        }
    }
    printf("---------------------\n");
    printf(temp);
    fclose(fp);

    //关闭文件后,重新以w方式打开文件,目的是清空文件
    //然后将含有计算结果的内容写入,这一整个流程下来,
    //就将计算结果和四则运算表达式一块写了进去
    fp = fopen("1.txt", "w");
    fputs(temp, fp);
    fclose(fp);
}

int main() {
    write_file();
    read_file();
    system("pause");
}
14.提取文件中所有数字并进行排序后重新写入文件

假设文件中数据如下

12
34
1
43
34
2
32
454
76
4565
3
43
2342
2
34
35
4564
6
54
65
67
5
52434
32
5
4
6
546
5
467
5
7
54
35
45
2
43
34
23
543
25
43
6
54
645
67
54
76
57
56
78
4
63
4543
452
3
4
32
42
34
23
4
35
43
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

void read_file() {
    FILE *fp = fopen("1.txt", "r");

    char buf[1024];
    int temp[1024 * 4] = { 0 };
    int i = 0;
    while (1) {
        memset(buf, 0, sizeof(buf));
        fgets(buf, sizeof(buf), fp);

        if (strlen(buf) > 0) {
            int a;
            sscanf(buf, "%d", &a);
            //printf("a= %d\n", a);
            temp[i] = a;
            i++;
        }
        if (feof(fp)) {
            break;
        }
    }

    //数组元素个数
    int n = i;
    printf("数组元素个数:%d\n", n);

    printf("排序前的数组:");
    for (i = 0; i < n; i++) {
        printf("%d,", temp[i]);
    }

    int j = 0;
    int k = 0;

    //排序
    int value = 0;
    for (j = 0; j < n; j++) {
        for (k = 0; k < n - 1 - j; k++) {
            if (temp[k] > temp[k + 1]) {
                value = temp[k];
                temp[k] = temp[k + 1];
                temp[k + 1] = value;
            }

        }
    }
    printf("\n排序后的数组:");
    for (i = 0; i < n; i++) {
        printf("%d,", temp[i]);
    }

    if (fp != NULL) {
        fclose(fp);
        fp = NULL;
    }

    //重新打开文件写入
    char arr[1024 * 4] = {0};
    fp = fopen("1.txt", "w");
    for (i = 0; i < n; i++) {
        sprintf(buf, "%d,", temp[i]);
        strncat(arr, buf, sizeof(buf));
    }
    fputs(arr, fp);
    if (fp != NULL) {
        fclose(fp);
        fp = NULL;
    }
}

int main() {
    read_file();
    system("pause");
}

打印结果
数组元素个数:64
排序前的数组:12,34,1,43,34,2,32,454,76,4565,3,43,2342,2,34,35,4564,6,54,65,67,5,52434,32,5,4,6,546,5,467,5,7,54,35,45,2,43,34,23,543,25,43,6,54,645,67,54,76,57,56,78,4,63,4543,452,3,4,32,42,34,23,4,35,43,
排序后的数组:1,2,2,2,3,3,4,4,4,4,5,5,5,5,6,6,6,7,12,23,23,25,32,32,32,34,34,34,34,34,35,35,35,42,43,43,43,43,43,45,54,54,54,54,56,57,63,65,67,67,76,76,78,452,454,467,543,546,645,2342,4543,4564,4565,52434,请按任意键继续. . .


1.txt文件中:
1,2,2,2,3,3,4,4,4,4,5,5,5,5,6,6,6,7,12,23,23,25,32,32,32,34,34,34,34,34,35,35,35,42,43,43,43,43,43,45,54,54,54,54,56,57,63,65,67,67,76,76,78,452,454,467,543,546,645,2342,4543,4564,4565,52434,

15.fprintf() [按照格式化文件写]
#include <stdio.h>
int fprintf(FILE *stream,const char &format,...);

根据参数format来转换并格式化数据,然后将结果输出到stream指定的文件中,指定出现字符串结束符为止

对比一下:
printf :操作屏幕 stdout流,向屏幕输出内容
sprintf :格式化字符串保存在数组中
fprintf :格式化字符串写入文件中

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include<time.h>

int main() {
    //以写的方式打开文件
    FILE *fp = fopen("1.txt","w");

    //fprintf写入
    int i = 0;
    int n = 10;
    int num = 0;

    //设置随机种子
    srand((unsigned int)time(NULL));

    for (i = 0; i < n; i++) {
        //产生10个1000以内的随机数
        num = rand() % 1000;
        //输出到屏幕
        //printf("%d\n", num);
        //比fputc fputs更方便,如果使用他们,需要先以只读的方式打开文件
        //然后都到一个写入一个,而使用fprintf则可以一步到位
        //输出到文件
        fprintf(fp, "%d\n", num);
    }

    //关闭文件
    if (fp != NULL)
    {
        fclose(fp);
        fp = NULL;
    }
    system("pause");
}
16.fscanf [按照格式化文件读](同样注意feof文件结尾产生的问题)

从文件中按照某种格式读取内容,fpintf是将内容按某种格式写入文件

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main() {
    //以写的方式打开文件
    FILE *fp = fopen("1.txt","r");

    int num;
    while (1) {
        fscanf(fp, "%d\n", &num);
        printf("num = %d\n", num);

        if (feof(fp))
        {
            break;
        }
    }
    //关闭文件
    if (fp != NULL)
    {
        fclose(fp);
        fp = NULL;
    }
    system("pause");
}
17.fwrite[按照块写文件]
#include <stdio.h>
size_t fwrite(const void*ptr,size_t size,size_t nmemb,FILE*stream);

以数据块的方式给文件写入内容
ptr:准备写入文件数据的地址
size: size_t 为unsigned int类型,此参数指定写入文件内容的块数据大小
nmemb:写入文件的块数,写入文件数据总大小为size*nmemb
stream:已经打开的文件指针
执行成功返回写入文件数据的块数目,和nmemb相等
失败返回0

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

//定义一个结构体,sizeof = 60
typedef struct Student {
    int age;
    char name[50];
    int score;
} Student;

int main() {
    FILE *fp = fopen("1.txt", "w");
    Student s = { 18,"zhangsan",59 };

    //结构体大小为60,使用下边几种方式把结构体中内容写入文件,你会发现
    //结果是相同的,使用fwrite进行写入,是按照一块一块写的,你可以分成
    //好多块去写,但是最终的结果都是一样的

    //int result = fwrite(&s, 15, 4, fp);
    //int result = fwrite(&s, 20, 3, fp);
    //int result = fwrite(&s, 1,sizeof(Student), fp);
    int result = fwrite(&s, sizeof(Student), 1, fp);
    printf("result=%d,sizeof(Student)=%d\n", result, sizeof(Student));
    fclose(fp);
    system("pause");
}
18.fread[按照块读文件]
#include <stdio.h>
size_t fread(void *ptr,size_t size,size_t nmemb,FILE *stream);

以数据块的方式从文件中读取内容
ptr:存放读取出来的数据的内存空间
size:size_t为unsigned int 类型,此参数指定读取文件内容的块数据大小
nmemb:读取文件的块数,读取文件总数据大小为:size*nmemb
stream:已经打开的文件指针

执行成功,返回实际成功读取到内容的块数,如果此值比nmemb小,但大于0,说明督导文件的结尾,失败则返回0

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

//定义一个结构体,sizeof = 60
typedef struct Student {
    int age;
    char name[50];
    int score;
} Student;

int main() {

    //写入
    FILE *fp = fopen("1.txt", "w");
    Student s = { 18,"zhangsan",59 };
    int result = fwrite(&s, sizeof(Student), 1, fp);
    printf("result=%d,sizeof(Student)=%d\n", result, sizeof(Student));
    fclose(fp);

    //读取
    FILE *fp2 = fopen("1.txt", "r");
    Student s2[10];

    int net = fread(s2, sizeof(Student), 1, fp2);
    printf("net = %d\n", net);

    printf("%d,%s,%d\n", s2->age, s2->name, s2->score);
    system("pause");
}
19.fread fwrite结合实现拷贝命令

先使用gcc编译成可执行文件,然后就可以在Linux命令行中使用命令拷贝了。这是在Linux命令行下的代码,可以执行,那么如果在windows确是优点差别的,Windows下需要将打开文件的r w改为rb wb才行,并且这种方式在Linux下也是可行的,所以为了兼容两个平台,最好将r w 都写作rb wb。为什么r 和w的写法在windows不行呢,因为如果不是rb wb Windows在打开文件的时候会把文件当作文本文件处理,所以如果遇到特殊字符就会有问题

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

/*
    模仿Linux cp命令 cp 源文件 目标文件
        第一个参数是不需要我们传入的,我们传入的参数
        会被放入argv这个数组中                
*/
int main(int argc,char *argv[]) {

    //第一个参数是cp 第二个参数是要拷贝的源文件路径
    //第三个参数是要拷贝到的目标文件
    //argc表示参数的个数
    if (argc != 3)
    {
        printf("error:参数不正确");
        return 0;
    }
    //打开源文件
    FILE *src = fopen(argv[1], "r");
    //打开目标文件
    FILE *dst = fopen(argv[2], "w");

    //从源文件中读取内容写入到目标文件
    char buf[4 * 1024];
    int len;
    while (1) {
        len = fread(buf, 1, sizeof(buf), src);
        printf("len = %d\n", len);
        if (len == 0) {
            break;
        }
        fwrite(buf, 1, len, dst);
    }
    //关闭文件
    fclose(src);
    fclose(dst);
    system("pause");
}
20.Linux和Windows下文本的区别

为什么会发生上边那种问题,其实是因为Windows和Linux针对文本的不同处理导致的

b是二进制模式的意思,b只是在Windows有效,在Linux用r和rb的结果是一样的
Unix和Linux下所有的文本文件行都是\n结尾,而Windows所有文本文件都是\r\n结尾
在Windows平台下,以文本的方式打开文件时,不加b:
当读取文件的时候,系统会将所有的\r\n转换成\n
当写入文件的时候,系统会将\n转换成\r\n写入
当以二进制的方式打开文件,则读写都不会进行这样的转换
在Unix/Linux平台下,文本与二进制模式没有区别,\r\n作为两个字符原样输入输出
21.判断当前平台是Windows还是Linux
1.png
22.文件的随机读写
fseek
#include <stdio.h>
int fseek(FILE *stream,long offset,int whence);

功能:移动文件流(文件光标)的读写位置
参数:stream 已经打开的文件指针
offset 根据whence来移动的位移数(偏移量),可以是正数也可以是负数,如果是整数,则相对于whence往右移动,如果是负数,则相对于whence往左移动,如果向前移动的字节数超大过了文件开头则出错返回,如果向右移动的字节数超过了文件末尾,再次写入时将增大文件尺寸
whence 取值如下
SEEK_SET 从文件开头移动offset个字节
SEEK_CUR 从当前位置移动offset个字节
SEEK_END 从文件末尾移动offset个字节
返回值:成功0,失败-1

fseek(fp,0,SEEK_SET) :在开头偏移0个字节,也就是回到文件开始的位置
fseek(fp,100,SEEK_SET):在开头向右偏移100个字节

fseek(fp,100,SEEK_CUR):在当前位置向右偏移100个字节
fseek(fp,-100,SEEK_CUR):在当前位置左右偏移100个字节

fseek(fp,0,SEEK_END):移动到文件结尾
#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

int main(int argc,char *argv[]) {
    FILE *fp = fopen("1.txt","r");
    //文件中写了一个单词hello,偏移一个后读取,得到的就是e
    fseek(fp, 1, SEEK_SET);
    char arr[100];
    fread(arr, 1, 1, fp);
    printf("读取到一个字节:%c\n", arr[0]);

    fseek(fp, 0, SEEK_SET);
    fread(arr, 1, 1, fp);
    printf("回到开头后读取到一个字节:%c\n", arr[0]);

    char arr2[100];
    fseek(fp, 4, SEEK_SET);
    fread(arr2, 1, 1, fp);
    printf("读取到最后一个字节:%c\n", arr2[0]);

    rewind(fp);
    char arr3[100];
    fseek(fp, 0, SEEK_CUR);
    fread(arr3, 1, 1, fp);
    printf("rewind读取到最一个字节:%c\n", arr3[0]);

    //获取文件大小要将光标移动到最后
    fseek(fp, 0, SEEK_END);
    printf("文件大小:%d\n",ftell(fp));
    fclose(fp);
    system("pause");
}
ftell
#include <stdio.h>
long ftell(FILE *stream);

获取文件流(文件光标)的读写位置
参数
stream 已经打开了的文件指针
返回值
成功 返回当前文件流位置
失败-1

#######rewind

#include <stdio.h>
void rewind(FILE *stream);

把文件流的读写位置移动到文件开头

23.获取文件状态
#include <sys/types.h>
#include <sys/stat.h>
int stat(const char *path,struct stat *buf);

struct stat
{
    dev_t st_dev;   //文件的设备编号
    ino_t st_ino;    //节点
    mode_t st_mode; //文件的类型和存取的权限
    nlink_t st_nlink; //连到该文件的硬链接数据,刚建立的文件值为1
    uid_t st_uid;  //用户id
    gid_t st_gid; //组id
    dev_t st_rdev; //设备类型,若此文件为设备文件,则为其编号
    off_t st_size; //文件字节数(文件大小)
    unsigned long st_blksize;//块大小(文件系统的IO缓冲区大小
    unsigned long st_blocks;//块数
    time_t st_atime; //最后一次访问时间
    time_t st_mtime;//最后一次修改时间
    time_t st_ctime;//最后一次改变时间,指属性

};

获取文件的状态信息
参数 path 文件名
buf 保存文件信息的结构体
返回值成功0 失败-1

删除文件,重命名文件
#include <stdop.h>
int remove(const char *pathname);

删除文件
参数pathname:文件名
返回值:0成功 -1失败

#include <stdio.h>
int rename (const char *oldpath,const char *newpath);

把oldpath的文件名改为newpath
成功0 失败-1

刷新缓冲区
#include <stdio.h>
int fflush(FILE *stream);

更新缓冲区,让缓冲区数据立马写入文件中
成功0 失败-1
未完,待续....

上一篇 下一篇

猜你喜欢

热点阅读