文件 IO 操作

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

文章目录

一 文件

C把文件看作是一系列连续的字节每个字节都能被单独读取。C提供两种文件模式文本模式 和 二进制模式。

1.1 文本模式

如果文件最初使用二进制编码的字符例如ASCLL 码 或 Unicode表示文本就像C字符串一样该文件就是文本文件其中包含文本内容。

  • 程序所见的内容和实际内容不同。
  • 读把本地环境表示的行末尾或文件结尾映射成C模式\r 转为 \n
  • 写(\n 转为 \r)

1.2 二进制模式

如果文件中的二进制值代表机器语言代码或数值数据使用相同的内部表示假设用于 long 或 double 类型的值或图片或音乐编码改文件就是二进制文件其中包含二进制内容。

  • 程序可以访问文件的每个字节
  • 若是写旧式 Mac 格式、MS-DOS格式或UNIX/Linux 格式的文件模式程序应该用二进制模式这样程序才能确定实际的文件内容并执行相应的动作。

二 函数

2.1fopen()

FILE *fopen(const char *pathname, const char *mode);

在这里插入图片描述

2.2 getc() 和 putc()

int getc(FILE *stream);
//fgetc(), getc() and getchar() return the character read as an unsigned char cast to an int or EOF on end of file or error.
//fgets() returns s on success, and NULL on error or when end of file occurs while no characters have been read.
//ungetc() returns c on success, or EOF on error.

int putc(int c, FILE *stream);
//fputc(), putc() and putchar() return the character written as an unsigned char cast to an int or EOF on error.
//puts() and fputs() return a nonnegative number on success, or EOF on error.

与getchar() 和 putchar() 类似不过要告诉getc() 和 putc() 函数使用哪一个文件。
例将文件A 中的数据放在文件B中

#include<stdio.h>
#include<stdlib.h>

int main(int argc,char* argv[])
{
    char ch;
    FILE* fp1;
    FILE* fp2;

    if(argc != 3)
    {
        printf("参数错误\n");
        return 0;
    }

    fp1 = fopen(argv[1],"r");
    fp2 = fopen(argv[2],"w");

    while( (ch = getc(fp1)) != EOF)
    {
        putc(ch,fp2);
    }

    fclose(fp1);
    fclose(fp2);


    return 0;
}

例子二简单的文件压缩保留每三个字符中的第一个

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define LEN 40

int main(int argc,char* argv[])
{
    FILE* in;
    FILE* out;

    int ch;
    char name[LEN];
    int count = 0;

    if(argc < 2)
    {
        fprintf(stderr,"Usage: %s filename\n",argv[0]);
        exit(EXIT_FAILURE);
    }

    //设置输入
    if( (in = fopen(argv[1],"r")) == NULL)
    {
        fprintf(stderr,"i coulun't open thr file \"%s\"\n",argv[1]);
        exit(EXIT_FAILURE);
    }

    //设置输出
    strncpy(name,argv[2],LEN-5);
    name[LEN-5] = '\0';
    strcat(name,".red"); //在文件名后添加 .red后缀
    if( (out = fopen(name,"w")) == NULL)
    {
        fprintf(stderr,"can't careate output file.\n");
        exit(3);
    }

    //拷贝数据
    while( (ch = getc(in)) != EOF)
    {
        if(count++ % 3 == 0)
        {
            putc(ch,out);
        }
    }

    if(fclose(in)!=0 || fclose(out)!=0)
    {
        fprintf(stderr,"Error incloseing files\n");
    }


    return 0;
}
//拷贝函数
char *strncpy(char *dest, const char *src, size_t n);

//尾部添加字符strcat
char *strncat(char *dest, const char *src, size_t n);

fprintf()和printf() 类似fprintf() 的第1个参数必须是一个文件指针。程序中的stderr指针把错误消息发至标准错误C标准通常这么做。

2.3 fclose()

int fclose(FILE *stream);
//Upon successful completion, 0 is returned.  Otherwise, EOF is returned and errno is set to indicate the error.  In either case,  
//any  fur‐ther access (including another call to fclose()) to the stream results in undefined behavior.
//即成功返回0

fclose(fp)函数关闭fp指向的文件必要时刷新缓冲区。对于比较正式的程序应该检查是否关闭成功。如果关闭成功返回0否则返回EOF。
注磁盘满、移动硬盘被移出或出现I/O错误都会导致fclose()函数失败

2.4 fprintf() 和 fscanf()

fprintf()和printf() 类似fscanf() 和 scanf()类似。区别在于第一个参数指定待处理的文件fprintf() 的第1个参数必须是一个文件指针。程序中的stderr指针把错误消息发至标准错误C标准通常这么做。

int fprintf(FILE *stream, const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);

例子

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define LEN 41

int main(int argc,char* argv[])
{
    FILE* fp;
    char words[LEN];

    if( (fp = fopen("wordy","a+")) == NULL)
    {
        fprintf(stdout,"can't open file\n");
        exit(EXIT_FAILURE);
    }

    puts("enter words to add to the file;press the #");
    puts("key at the beginning of a line to terminate.");
    while( (fscanf(stdin,"%40s",words) == 1) && (words[0] != '#'))
    {
        fprintf(fp,"%s\n",words);
    }

    puts("File contenets:");
    rewind(fp);
    while(fscanf(fp,"%s",words) == 1)
    {
        puts(words);
    }

    puts("Dones");
    if(fclose(fp) != 0)
    {
        fprintf(stderr,"close file failed\n");
    }

    return 0;
}

从标准输入获取单词然后输出到另一个文件
将 fscanf()第一个参数改为文件描述符既可从文件读入

 rewind(fp); //rewind接收一个文件指针作为参数移动到文件开始处。

在这里插入图片描述

2.5 fgets() 和 fputs()

第一个参数fgets也和gets函数一样也是表示存储输入位置的地址char*类型。
第二个参数是一个整数代表待输入字符串的大小。
最后一个参数是一个文件指针指定待读取的文件。
用法fgets(buf,STLEN,fp);

fgets()函数读取输入直到第一个换行符后面或者读到文件末尾或者读取 STLEN-1 个字符然后fgets() 在末尾添加一个空字符使之成为一个字符串。字符串大小其实是字符数加上一个空字符。

若fgets()在读到字符上线之前已读完一整行它会吧表示行结尾的换行符放在空字符前面。
fgets(函数在遇到EOF时将返回NULL值可以利用这一机制检查是否到达文件末尾。

fputs() 接受两个参数第一个是字符串的地址第二个是文件指针。该函数根据传入地址找到的字符串写入指定的文件中。和puts函数不通fputs(在打印字符串时不会在其末尾添加换行符。

fput(buf,fp);

char *fgets(char *s, int size, FILE *stream);
int fputs(const char *s, FILE *stream);

2.6 rewind()

void rewind(FILE *stream);

返回到文件开始位置

2.7 fseek() 和 ftell()

Fseek 可把文件当做数据在fopen打开的文件中直接移动到任意字节处。
ftell()函数返回一个long类型的值标识文件当前位置。

int fseek(FILE *stream, long offset, int whence);
//If whence is set to SEEK_SET, SEEK_CUR, or SEEK_END, the  offset  isrelative  to  the start of the file, the current position indicator, or end-of-file, respectively

long ftell(FILE *stream);
//返回值类型为long返回的是参数指向的文件的当前位置距文件开始处的字节数

fseek第三个参数 whence 若是选择为 SEEK_SET,SEEK_CUR,SEEK_END,可以将文件指针偏移到文件开始当前位置文件末尾等位置。

例子文件内容逆向打印

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define CNTL_Z '\032'

#define SLEN 81

int main(int argc,char* argv[])
{
    char file[SLEN];
    char ch;
    FILE* fp;
    long count,last;

    puts("enter the name of the file to be processed:");
    scanf("%s",file);

    if( (fp = fopen(file,"rb")) == NULL)
    {
        printf("reverse can't open %s\n",file);
        exit(EXIT_FAILURE);
    }

    fseek(fp,0L,SEEK_END); //定位到文件末尾

    last = ftell(fp);
    for(count  = 1L;count <= last; count++)
    {
        fseek(fp,-count,SEEK_END);  //回退
        ch = getc(fp);
        if(ch != CNTL_Z && ch != '\r')
        {
            putchar(ch);
        }
    }

    putchar('\n');
    
    fclose(fp);

    return 0;
}

fseek(fp,0L,SEEK_SET); //文件开始处
fseek(fp,10L,SEEK_SET); //文件中第10个字节
fseek(fp,2L,SEEK_CUR); //从当前位置往前移两个字节
fseek(fp,0L,SEEK_end); //定位到文件末尾
fseek(fp-10L,SEEK_end);//从文件末尾回退10个字节

若是一切正常fseek(返回0如果出现错误如试图移动的距离超过文件的范围返回 -1

2.8 fflush()

调用fflush() 函数引起输出缓冲区中所有的未写入数据被发送到fp指定的输出文件。
这个过程称为刷新缓冲区。如果fp是空指针所有输出缓冲区都会被刷新。在输入流中使用fflush函数的效果是未定义的。只要最近一次操作不是输入操作就可以用该函数来更新流(任何读写模式)

int fflush(FILE *stream)

2.9 fgetpos() 和 fsetpos()

fseek() 和 ftell() 潜在的问题是他们都把文件大小限制在long类型能表示的范围内。

随着存储设备容量迅猛增长文件也越来越大。
鉴于此ANSIC新增了两个处理较大文件的新定位函数fgetpos() 和 fsetpof()

这两个函数不适用long类型表示位置而是使用一种新的类型:fpos_tfile position type,文件定位类型。fpos_t类型是不是基本类型。

fpos_t 类型的变量或数据对象可以在文件中指定一个位置他不能是数组类型。除此之外没有其他限制。

int fgetpos(FILE *stream, fpos_t *pos);
//把fpos_t类型的值放在pos指向的位置上该值描述了文件中的当前位置距文件开头的字节数。如果成功fgetpos()函数返回0如果失败返回非0

int fsetpos(FILE *stream, const fpos_t *pos);
//使用pos指向位置上的fpos_t类型值来设置文件指针指向偏移该值后指定的位置。
成功返回0失败返回非0.
fpot_t类型的值应该通过之前调用fgetpos获得

2.10 feof() 和 ferror()

int feof(FILE *stream);
int ferror(FILE *stream);

如果标准输入函数返回EOF则通常表明函数已经到达文件末尾了。然而出现错误读取时函数也会返回EOF。

feof() 和 ferror() 函数用于区分两种情况。
当上一次输入调用检测到文件末尾时feof() 返回一个非零值否则返回0
当读或写出现错误ferror() 函数返回一个非零值否则返回0

2.11 ungetc()

int ungetc() 函数把C指定的字符放回输入流中如果吧一个字符返回输入流下次调用标准输入函数时将读取该字符。
例如假设要读取下一个冒号前所有的字符但是不包括冒号本身可以使用getchar() 或 getc() 函数读取到冒号然后使用ungetc() 把冒号放回输入流中。

ANSI C标准保证每次只会放回一个字符如果实现允许把一行中的多个字符放回输入流那么下次输入函数读入的字符顺序就会与放回去的顺序相反。

char *fgets(char *s, int size, FILE *stream);

2.12 setvbuf()

 int setvbuf(FILE *stream, char *buf, int mode, size_t size);

2.13 fread() 和 fwrite()

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
//返回成功读入项的数量。正常返回值就是nmemb但如果出现写入错误就会比nmenb小。

size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
//返回成功写入项的数量。正常返回值就是nmemb但如果出现写入错误就会比nmenb小。

背景之前用到的IO函数都是面向文本的用于处理字符和字符串。如何在文件中存储数值用fprintf() 函数 和 %f 转换说明只是把数据保存为字符串。
例如

double num = 1.0/3;
fprintf(fp,"%f",num);
//fprintf(fp,"%.2f",num);
//fprintf(fp,"%.12f",num);

把num存储为8个字符0.333333。使用%.2f说明将其存储为4个字符使用%.12f说明把他存储为14个字符。

为了保证数值在存储前后一致最精确的做法是使用与计算机相同的位组合来存储。因此double类型的值就应该存在一个double大小的单元中。

如果以程序所用的表示法把数据存储在文件中则称以二进制形式存储数据。

二进制和文本的用法很容易混淆。
可以用二进制模式打开文本格式的文件可以把文本存储在二进制形式的文件中。可以调用getc拷贝包含二进制数据的文件。然而**一般而言用二进制模式在二进制格式文件中存储二进制数据。**类似的最常用的还是以文本格式打开文本文件中的文本数据。

  • size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
    fwrite()函数是把二进制数据写入文件。size_t是根据C类型定义的类型是sizeof运算符返回的类型通常是unsigned int.
    ptr 是待写入数据块的地址。
    size 表示待写入数据块的大小(以字节为单位)
    nmemb表示待写入数据块的数量。

例1将一块256字节数据从buffer写入文件

char buf[256];
fwrite(buffer,256,1,fp);

例2
保存一个内含10个double类型的数组

double earnings[10];
fwrite(earnings,sizeof(double),10,fp);

数据被分为10块每块都是double的大小。

注第一个类型不是固定类型而是void*,

  • size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
    接收参数与fwrite()一致.
    ptr是指待读取文件在内存中的地址
    fp指待读取的文件。
    该函数用于读取被fwrite()写入文件的数据。

例恢复上例中保存的10个double类型的数据可以这么做

double earnings[10];
fread(earnings,sizeof(double),10,fp);
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6