C Language Review: File Operations

File Operation Topic

C language file reading and writing concepts

File classification

  • According to the logical structure of the file:
    • Record file: consists of records with a certain structure (fixed and variable length)
    • Streaming file: consists of one character (byte) data sequence
  • By storage medium:
    • Ordinary file: storage media file (disk, tape, etc.)
    • Device files: non-storage media (keyboards, monitors, printers, etc.)
  • By data organization:
    • Text file: ASCII file, each byte stores the ASCII code of one character
    • Binary file: data is stored as it is stored in memory

  • Each file is identified by a filename, and the filename of an I/O device is system-defined, such as:
  • COM1 or AUX - first serial port, additional equipment
  • COM2 - the second serial port, in addition, there may be COM3, COM4, ​​etc.
  • CON - console (console), keyboard (for input) or display (for output)
  • LPT1 or PRN - first parallel port or printer
  • LPT2 - the second parallel port, there may also be LPT3, etc.
  • NUL - empty device (not NULL)
  • The disk file can be named by the user, but the device name reserved by the system (both under windows and dos) cannot be used as a file name, such as a file cannot be named CON (without extension) or CON.TXT (without extension) with extension).
  • Stream Concept:
  • Stream is a dynamic concept, and a byte can be likened to a drop of water. The transmission of bytes between devices, files and programs is a stream, similar to the transmission of water in a pipe. It can be seen that the stream is the input An abstraction of the output source and an abstraction of the transmitted information. By abstracting input and output sources, the differences between devices are shielded, so that programmers can perform storage operations in a common way, and by abstracting transmission information, all information is converted into byte streams for transmission. The process of information interpretation is separated from the transmission process.
  • In the C language, I/O operations can be simply viewed as moving bytes in or out of a program, and this process of moving is called a stream. The program only needs to care whether the byte data is output correctly, and whether the byte data to be read is input correctly, the details of the specific I/O device are hidden from the programmer.
  • file handling method
  • Buffer file system: Advanced file system, the system automatically opens up memory buffers for files in use
  • Unbuffered file system: A low-level file system where the user sets a buffer for each file in the program
  • Buffered filesystem understanding: file handles
  • 补充一个:查看当前系统的IO缓冲区大小是通过宏BUFSIZ,stdio.h中找,可以看到大小。
 

 

文件操作API

文件api的分类

  1. 文件读写api

fgetc int fgetc( FILE *stream );

fputc int fputc( int ch, FILE *stream );

按照字符读写文件

char c;

FILE fd;

ch=fgetc(fd);

fd=fputc(c,fd); //假设fd已经被读写方式打开

fputc()会返回写入成功的字符, 若返回EOF(-1) 则代表写入失败

getc()会返回读取到的字符, 若返回EOF 则表示到了文件尾.

fputs int fputs( const char *str, FILE *stream );

fgets char *fgets( char *str, int num, FILE *stream );

按照行读写文件 (读写配置文件)

char ch[1024];

fgets(ch,sizeof(ch),fd)

fputs(ch,fd);

fgets()成功,则返回第一个参数str,失败返回NULL;

fputs()成功返回写入的字符数;失败返回EOF

fread int fread( void *buffer, size_t size, size_t num, FILE *stream );

fwirte int fwrite( const void *buffer, size_t size, size_t count, FILE *stream );

按照块读写文件 (大数据块迁移)

fread(ch,sizeof(char),sizeof(ch)-1,fd);

fwrite(ch,sizeof(char),sizeof(ch)-1,fd);

返回值:返回实际写入的数据块数目(参数3);若是返回值大于参数3,则发生错误。

我尝试过利用fread函数赋值二维数组,但是失败了.

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

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

按照格式化进行读写文件

fprintf(fd,"%s",ch);

fscanf(fd,"%c",c)

成功返回工作的数目;失败返回负数

 

    02)文件控制api    

        文件是否结束

        文件指针的定位、跳转

        fseek(fp, 0L, SEEK_END); //把文件指针从0位置开始,移动到文件末尾(宏的作用),那个0L是将0转化为long型

            宏: 偏移起始位置:文件头0(SEEK_SET),当前位置1(SEEK_CUR),文件尾2(SEEK_END)

            //获取文件长度;

        得到文件位置指针当前位置相对于文件首的偏移字节数。

        length = ftell(fp);

        fseek(fp, 0L, SEEK_SET);

标准文件的读写

1.文件的打开fopen()

文件的打开操作表示将给用户指定的文件在内存分配一个FILE结构区,并将该结构的指针返回给用户程序,以后用户程序就可用此FILE指针来实现对指定文件的存取操作了。当使用打开函数时,必须给出文件名、文件操作方式(读、写或读写),如果该文件名不存在,就意味着建立(只对写文件而言,对读文件则出错),并将文件指针指向文件开头。若已有一个同名文件存在,则删除该文件,若无同名文件,则建立该文件,并将文件指针指向文件开头。

fopen(char *filename,char *type);

其中*filename是要打开文件的文件名指针,一般用双引号括起来的文件名表示,也可使用双反斜杠隔开的路径名。而*type参数表示了对打开文件的操作方式。其可采用的操作方式如下:

 

方式 含义

"r" 打开,只读

"w" 打开,文件指针指到头,只写

"a" 打开,指向文件尾,在已存在文件中追加

"rb" 打开一个二进制文件,只读

"wb" 打开一个二进制文件,只写

"ab" 打开一个二进制文件,进行追加

"r+" 以读/写方式打开一个已存在的文件

"w+" 以读/写方式建立一个新的文本文件

"a+" 以读/写方式打开一个文件文件进行追加

"rb+" 以读/写方式打开一个二进制文件

"wb+" 以读/写方式建立一个新的二进制文件

"ab+" 以读/写方式打开一个二进制文件进行追加

 

当用fopen()成功的打开一个文件时,该函数将返回一个FILE指针,如果文件打开失败,将返回一个NULL指针。如想打开test文件,进行写:

 

FILE *fp;//声明指针,而不是实例.

if((fp=fopen("test","w"))==NULL)

{

printf("File cannot be opened\n");

exit();

}

else

printf("File opened for writing\n");

……

fclose(fp);

 

    DOS操作系统对同时打开的文件数目是有限制的,缺省值为5,可以通过修改CONFIG.SYS文件改变这个设置。

 

2.关闭文件函数fclose()

文件操作完成后,必须要用fclose()函数进行关闭,这是因为对打开的文件进行写入时,若文件缓冲区的空间未被写入的内容填满,这些内容不会写到打开的文件中去而丢失。只有对打开的文件进行关闭操作时,停留在文件缓冲区的内容才能写到该文件中去,从而使文件完整。再者一旦关闭了文件,该文件对应的FILE结构将被释放,从而使关闭的文件得到保护,因为这时对该文件的存取操作将不会进行。文件的关闭也意味着释放了该文件的缓冲区。

int fclose(FILE *stream);

它表示该函数将关闭FILE指针对应的文件,并返回一个整数值。若成功地关闭了文件,则返回一个0值,否则返回一个非0值。常用以下方法进行测试:

 

if(fclose(fp)!=0)

{

printf("File cannot be closed\n");

exit(1);

}

else

printf("File is now closed\n");

 

当打开多个文件进行操作,而又要同时关闭时,可采用fcloseall()函数,它将关闭所有在程序中打开的文件。

int fcloseall();//关闭除标准流(stdinstdoutstderrstdprnstdaux)之外的所有打开的流,刷新所有的流缓冲区,并返回关闭的流数。

该函数将关闭所有已打开的文件,将各文件缓冲区未装满的内容写到相应的文件中去,接着释放这些缓冲区,并返回关闭文件的数目。如关闭了4个文件,则当执行:

n=fcloseall(); 时,n应为4。

 

3.文件的读写

(1).读写文件中字符的函数(一次只读写文件中的一个字符)

 

int fgetc(FILE *stream);

int fgetchar(void);

int fputc(int ch,FILE *stream);

int fputchar(int ch);

int getc(FILE *stream);

int putc(int ch,FILE *stream);

其中fgetc()函数将把由流指针指向的文件中的一个字符读出,例如:

ch=fgetc(fp);

将把流指针fp指向的文件中的一个字符读出,并赋给ch,当执行fgetc()函数时,若当时文件指针指到文件尾,即遇到文件结束标志EOF(其对应值为-1),该函数返回一个-1给ch,在程序中常用检查该函数返回值是否为-1来判断是否已读到文件尾,从而决定是否继续。

 

#include "stdio.h"

main()

{

    FILE *fp;

    char ch;

    if((fp=fopen("myfile.tex","r"))==NULL)

    {

        printf("file cannot be opened\n");

        exit(1);

      

    while((ch=fgetc(fp))!=EOF) fputc(ch,stdout);

    close(fp);

}

 

该程序以只读方式打开myfile.txt文件,在执行while循环时,文件指针每循环一次后移一个字符位置。用fgetc()函数将文件指针指定的字符读到ch变量中,然后用fputc()函数在屏幕上显示,当读到文件结束标志EOF时,变关闭该文件。

上面的程序用到了fputc()函数,该函数将字符变量ch的值写到流指针指定的文件中去,由于流指针用的是标准输出(显示器)的FILE指针stdout,故读出的字符将在显示器上显示。又比如:

putc(ch,fp);

该函数执行结构,将把ch表示的字符送到流指针fp指向的文件中去。

在Turbo C编译器中,putc()等价于fput(),getc()等价于fgetc()。putchar(c)相当于fputc(c,stdout);getchar()相当于fgetc(stdin)。

注意,这里使用char ch,其实是不科学的,因为最后判断结束标志时,是看ch!=EOF,而EOF的值为-1,这显然和char是不能比较的。所以,某些使用,我们都定义成int ch。

(2).读写文件中字符串的函数

 

char *fgets(char *string,int n,FILE *stream);

char *gets(char *s);

int fprintf(FILE *stream,char *format,variable-list);

int fputs(char *string,FILE *stream);

int fscanf(FILE *stream,char *format,variable-list);

其中fgets()函数将把由流指针指定的文件中n-1个字符,读到由指针stream指向的字符数组中去,例如:

fgets(buffer,9,fp);

将把fp指向的文件中的8个字符读到buffer内存区,buffer可以是定义的字符数组,也可以是动态分配的内存区。

注意,fgets()函数读到'\n'就停止,而不管是否达到数目要求。同时在读取字符串的最后加上'\0'。

fgets()函数执行完以后,返回一个指向该串的指针。如果读到文件尾或出错,则均返回一个空指针NULL,所以长用feof()函数来测定是否到了文件尾或者是ferror()函数来测试是否出错,例如下面的程序用fgets()函数读test.txt文件中的第一行并显示出来:

 

#include "stdio.h"

main()

{

    FILE *fp;

    char str[128];

    if((fp=fopen("test.txt","r"))==NULL)

    {

        printf("cannot open file\n");

        exit(1);

    }

    while(!feof(fp))

    {

        if(fgets(str,128,fp)!=NULL) printf("%s",str);

    }

    fclose(fp);

}

gets()函数执行时,只要未遇到换行符或文件结束标志,将一直读下去。因此读到什么时候为止,需要用户进行控制,否则可能造成存储区的溢出。因此被认为不安全,逐渐不使用它。

fputs()函数想指定文件写入一个由string指向的字符串,'\0'不写入文件。

fprintf()和fscanf()同printf()和scanf()函数类似,不同之处就是printf()函数是想显示器输出,fprintf()则是向流指针指向的文件输出;fscanf()是从文件输入。

 

下面程序是向文件test.dat里输入一些字符:

 

#include<stdio.h>

main()

{

char *s="That's good news";

int i=617;

FILE *fp;

fp=fopne("test.dat", "w");

fputs("Your score of TOEFLis",fp);

fputc(':', fp);

fprintf(fp, "%d\n", i);

fprintf(fp, "%s", s);

fclose(fp);

}

用DOS的TYPE命令显示TEST.DAT的内容如下所示:

屏幕显示

Your score of TOEFL is: 617

That's good news

 

下面的程序是把上面的文件test.dat里的内容在屏幕上显示出来:

#include<stdio.h>

main()

{

char *s, m[20];

int i;

FILE *fp;

fp=fopen("test.dat", "r");

fgets(s, 24, fp);

printf("%s", s);

fscanf(fp, "%d", &i);

printf("%d", i);

putchar(fgetc(fp));

fgets(m, 17, fp);

puts(m);

fclose(fp);

getch();

}

运行后屏幕显示:

Your score of TOEFL is: 617

That's good news

 

4.清除和设置文件缓冲区

(1).清除文件缓冲区函数:

int fflush(FILE *stream);

int flushall();

 

fflush()函数将清除由stream指向的文件缓冲区里的内容,常用于写完一些数据后,立即用该函数清除缓冲区,以免误操作时,破坏原来的数据。

flushall()将清除所有打开文件所对应的文件缓冲区。

(2).设置文件缓冲区函数

void setbuf(FILE *stream,char *buf);

void setvbuf(FILE *stream,char *buf,int type,unsigned size);

这两个函数将使得打开文件后,用户可建立自己的文件缓冲区,而不使用fopen()函数打开文件设定的默认缓冲区。

对于setbuf()函数,buf指出的缓冲区长度由头文件stdio.h中定义的宏BUFSIZ的值决定,缺省值为512字节(不同的系统不一样,我的Linux测试是8192字节,还有,他就是BUFSIZ,没有E字母)。当选定buf为空时,setbuf函数将使的文件I/O不带缓冲(直接读写文件)。而对setvbuf函数,则由malloc函数来分配缓冲区。参数size指明了缓冲区的长度(必须大于0),而参数type则表示了缓冲的类型,其值可以取如下值:

 

type 值 含义

    _IOFBF 文件全部缓冲,即缓冲区装满后,才能对文件读写

    _IOLBF 文件行缓冲,即缓冲区接收到一个换行符时,才能对文件读写

_IONBF 文件不缓冲,此时忽略buf,size的值,直接读写文件,不再经过文件缓冲区缓冲

 

5.文件的随机读写函数

前面介绍的文件的字符/字符串读写,均是进行文件的顺序读写,即总是从文件的开头开始进行读写。这显然不能满足我们的要求,C语言提供了移动文件指针和随机读写的函数,它们是:

(1).移动文件指针函数

    long ftell(FILE *stream); 函数ftell()用来得到文件指针离文件开头的偏移量。当返回值是-1时表示出错。

    int rewind(FILE *stream); rewind()函数用于文件指针移到文件的开头,当移动成功时,返回0,否则返回一个非0值。

    fseek(FILE *stream,long offset,int origin); fseek()函数用于把文件指针以origin为起点移动offset个字节,其中origin指出的位置可有以下几种:

 

    origin 数值 代表的具体位置

 

SEEK_SET 0 文件开头

SEEK_CUR 1 文件指针当前位置

SEEK_END 2 文件尾

 

例如:

fseek(fp,10L,0);//0代表文件头

把文件指针从文件开头移到第10字节处,由于offset参数要求是长整型数,故其数后带L。

fseek(fp,-15L,2);//2代表文件尾

把文件指针从文件尾向前移动15字节。

 

(2).文件随机读写函数

 

int fread(void *ptr,int size,int nitems,FILE *stream);

int fwrite(void *ptr,int size,int nitems,FILE *stream);

 

fread()函数从流指针指定的文件中读取nitems个数据项,每个数据项的长度为size个字节,读取的nitems数据项存入由ptr指针指向的内存缓冲区中,在执行fread()函数时,文件指针随着读取的字节数而向后移动,最后移动结束的位置等于实际读出的字节数。该函数执行结束后,将返回实际读出的数据项数,这个数据项数不一定等于设置的nitems,因为若文件中没有足够的数据项,或读中间出错,都会导致返回的数据项数少于设置的nitems。当返回数不等于nitems时,可以用feof()或ferror()函数进行检查。

fwrite()函数从ptr指向的缓冲区中取出长度为size字节的nitems个数据项,写入到流指针stream指向的文件中,执行该操作后,文件指针将向后移动,移动的字节数等于写入文件的字节数目。该函数操作完成后,也将返回写入的数据项数。

 

非标准文件的读写

这类函数最早用于UNIX操作系统,ANSI标准未定义,但有时也经常用到,DOS 3.0以上版本支持这些函数。它们的头文件为io.h。

由于我们不常用这些函数,所以在这里就简单说一下。

1.文件的打开和关闭

open()函数的作用是打开文件,其调用格式为:

int open(char *filename, int access);

该函数表示按access的要求打开名为filename的文件,返回值为文件描述字,其中access有两部分内容:

基本模式和修饰符, 两者用" "("或")方式连接。修饰符可以有多个, 但基本模式只能有一个。

 

access的规定

--------------------------------------------------------

基本模式 含义 修饰符 含 义

--------------------------------------------------------

O_RDONLY 只读 O_APPEND 文件指针指向末尾

O_WRONLY 只写 O_CREAT 文件不存在时创建文件, 属性按基本模式属性

O_RDWR 读写 O_TRUNC 若文件存在, 将其长度缩为0, 属性不变

O_BINARY 打开一个二进制文件

O_TEXT 打开一个文字文件

---------------------------------------------------------

open()函数打开成功, 返回值就是文件描述字的值(非负值), 否则返回-1。

close()函数的作用是关闭由open()函数打开的文件, 其调用格式为:

int close(int handle);

该函数关闭文件描述字handle相连的文件。

 

2.读写函数

int read(int handle, void *buf, int count);

read()函数从handle(文件描述字)相连的文件中, 读取count个字节放到buf所指的缓冲区中,

返回值为实际所读字节数, 返回-1表示出错。返回0 表示文件结束。

 

write()函数的调用格式为:

int write(int handle, void *buf, int count);

write()函数把count个字节从buf指向的缓冲区写入与handle相连的文件中, 返回值为实际写入的字节数。

 

3.随机定位函数

lseek()函数的调用格式为:

int lseek(int handle, long offset, int fromwhere);

该函数对与handle相连的文件位置指针进行定位,功能和用法与fseek()函数相同。

 

tell()函数的调用格式为:

long tell(int handle);

该函数返回与handle相连的文件现生位置指针, 功能和用法与ftell()相同

注意点

文本文件: ASCII文件,每个字节存放一个字符的ASCII码

二进制文件:数据按其在内存中的存储形式原样存放

项目开发中参考fgets函数的实现方法

 

fgets(buf, bufMaxLen, fp);

对fgets函数来说,n必须是个正整数,表示从文件按中读出的字符数不超过n-1,存储到字符数组str中,并在末尾加上结束标志'\0',换言之,n代表了字符数组的长度,即sizeof(str)。如果读取过程中遇到换行符或文件结束标志,读取操作结束。若正常读取,返回指向str代表字符串的指针,否则,返回NULL(空指针)。

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324850282&siteId=291194637