当前位置: 首页 > article >正文

文件 IO 操作

文章目录

    • 一 文件
      • 1.1 文本模式
      • 1.2 二进制模式
    • 二 函数
        • 2.1fopen()
        • 2.2 getc() 和 putc()
        • 2.3 fclose()
        • 2.4 fprintf() 和 fscanf()
        • 2.5 fgets() 和 fputs()
        • 2.6 rewind()
        • 2.7 fseek() 和 ftell()
        • 2.8 fflush()
        • 2.9 fgetpos() 和 fsetpos()
        • 2.10 feof() 和 ferror()
        • 2.11 ungetc()
        • 2.12 setvbuf()
        • 2.13 fread() 和 fwrite()

一 文件

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_t(file 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);

http://www.kler.cn/a/15503.html

相关文章:

  • 【MySQL】优化方向+表连接
  • http响应码https的区别
  • 【Java多线程】单例模式(饿汉模式和懒汉模式)
  • 前后端请求响应
  • leetcode面试 150题之 三数之和 复刷日记
  • 【日常记录-Git】git log
  • jQuery知识点三
  • Linux命令集(Linux常用命令--cat指令篇)
  • 中级软件设计师备考---信息系统安全
  • 最新国内免费chatgpt 的试用方法
  • 首期smardaten无代码训练营圆满收官,两周内容精彩回顾!
  • 基于opencv的YOLOV3对图片的目标检测
  • 【力扣-141】 环形链表 + 【力扣-142】 环形链表 II
  • Flink之TaskManager内存解析
  • 美团赴抖音之“约”:让本地生活补贴大战来得更猛烈些?
  • win11安装双系统ubuntu20.04指导
  • Openwrt搭建php页面
  • 【MATLAB数据处理实用案例详解(19)】——利用神经模糊控制实现对洗衣机的控制
  • 几种编程语言之结构体内存对齐
  • IDA简单使用
  • 用Python分析周杰伦歌曲并进行数据可视化
  • node项目的建立
  • HTTP 知识点总结
  • MRI k空间概念整理
  • 怎么卸载现有Python?【全方位解决】
  • 1。C语言基础知识回顾