C语言-文件操作1

C语言中文件是什么
fopen函数的用法
fgetc和fputc的用法
fgets和fputs的用法

在操作系统中,为了统一对各种硬件的操作,简化接口,不同的硬件设备也都被看成一个文件,对这些文件操作就是对磁盘中的文件进行操作。

常用的硬件设备所对应的文件:

文件 硬件设备
stdin 标准输入文件,一般指键盘;scanf()、getchar()等函数默认从stdin获取输入。
stdout 标准输出文件,一般指显示器;printf()、putchar()等函数默认向stdout输出数据。
stderr 标准错误文件,一般指显示器;perror()等函数默认向stderr输出数据(下面会讲到)
stdprn 标准打印文件,一般指打印机

操作文件的正确流程:打开文件 –> 读写文件 –> 关闭文件。

所谓打开文件,就是获取文件的有关信息,例如文件名、文件状态、当前读写位置等,这些信息会被保存到一个FILE类型的结构体变量中。关闭文件就是断开与文件之间的联系,释放结构体变量,同时禁止再对该文件进行操作。

在C语言中,文件有多种读写方式,可以一个字符一个字符的读取,也可以读取一整行,加可以读取若干个字节,文件的读写位置非常的灵活,可以从文件开头读取,也可以从中间读取。

数据在文件与内存之间传递的过程叫做文件流,数据从文件复制到内存的过程叫输入流,从内存保存到文件的过程叫输出流。

或者我们换一种理解,文件只是数据源的一种,除了文件,还有数据库,网络,键盘等;数据传递到内存也就是保存到C语言的变量(例如整数、字符串、数组、缓冲区等)。我们把数据在数据源和程序(内存)之间传递的过程叫做数据流(Data Stream)。相应的,数据从数据源到程序(内存)的过程叫做输入流(Input Stream),从程序(内存)到数据源的过程叫做输出流(Output Stream)

输入输出(Input Output IO)是指程序(内存)与外部设备(键盘、显示器、磁盘、其他计算机等)进行交互操作,几乎所有程序都有输入和输出操作,如从键盘上读取数据,从本地网络上的文件读取数据或者写入数据等,通过输入和输出操作可以和外界接收信息,或者是把信息传到外界、

我们可以说,打开文件就是打开了一个流。

fopen函数的用法

利用<stdio.h>头文件中的fopen函数来打开文件:

FILE *fopen(char *filename, char *mode);

filename为文件名包括路径,mode为打开方式,它们都是字符串。

fopen函数的返回值

fopen函数会获取到文件信息,包括文件名、文件状态、当前读写位置等,并将这些信息保存到一个FILE类型的结构体变量中,然后将该结构体变量的地址返回。

FILE是<stdio.h>头文件中的一个结构体,它专门用来保存文件信息。我们不用关心FILE的具体结构,只需要知道它的用法即可。

当然如果我们希望接收到fopen函数的返回值就需要定义一个FILE结构体类型的变量指针,因为fopen函数返回的是一个地址,所以我们要想取到其中的值,我们就需要一个指针来存储其中的值。

FILE *fp = fopen("demo.txt", "r");

表示以“只读”的方式打开当前目录下的demo.txt文件,并使fp指向该文件,这样就可以通过操纵fp来操作demo.txt文件了,而这样的fp就称为文件指针

判断文件是否被打开

我们要想读写文件中的内容就必须先打开文件,而且必须保证文件必须被打开才能够确定我接下来的操作会被执行。

在文件打开出错时fopen函数将返回一个空指针,也就是说我们需要判断它读取完文件后,返回的指针是否为空指针来判断打开文件是否成功。

FILE *fp;//定义一个FILE结构体类型指针
if((fp = fopen("D:\\demo.txt", "rb")) == NULL){//判断是否为空指针
  printf("Fail to open file!\n");
  exit(0);//退出程序
}

以上代码是文件操作规范写法,读者再打开文件时一定要检查文件是否被打开,因为打开失败,会导致你之后的操作全都是无效的。往往都以结束程序告终。

fopen函数的打开方式

控制读写权限的字符串:

打开方式 说明
“r” 以"只读"的方式打开文件,只允许读取不允许写入。文件必须存在,否则打开失败
“w” 以"写入"的方式打开文件,如果文件不存在,则新建一个文件,若文件存在,清空源文件中的内容,重新写入。(相当于删除文件,在创建一个新文件)
“a” 以"追加"的方式打开文件,如果文件不存在,那么创建一个文件,如果文件存在则把写入的数据,追加到文件末尾(文件原有内容保留)
“r+” 以"读写"的方式打开文件,即可以读写也可以写入,也就是随意更新文件。但是要保证文件必须存在,否则打开失败。由于光标位于开头,所以会覆盖原有数据。
“w+” 以"写入/更新"的方式,相当于wr+叠加的效果。即可以读取也可以写入,也就是随意更新文件。如果文件不存在,那么创建一个新文件,如果文件存在将其中内容删除。
“a+” 以"追加/更新"的方式打开文件,相当于ar+叠加的效果。即可以读取也可以写入,也就是随意更新文件。如果文件不存在,那么创建一个文件;如果文件存在,那么把追加的内容写入文件末尾。

控制读写方式的字符串:

打开方式 说明
“t” 文本文件,如果不写默认为"t"
“b” 二进制文件。

调用fopen函数时必须指明权限,但是可以不指明读写方式。

读取方式和读写权限何以结合使用,但是必须将读写方式放在读写权限中间或者后面,就是不能放在开头。

  • 将读写方式放在读写权限的末尾:“rb”、“wt”、“ab”、“r+b”、“w+t”、“a+t”
  • 将读写方式放在中间:“rb+"、“wt+"、“ab+”

整体来说,文件打开方式各字符的含义:

  • r(read):读
  • w(write):写
  • a(append):追加
  • t(text):文本文件
  • b(binary):二进制文件
  • +:读和写

关闭文件 文件一旦使用文件,应该用fclose()函数将文件关闭,以释放相关资源,避免数据丢失。

int fclose(FILE *fp);
//fp为文件指针则可简写为
fclose(fp)

文件正常关闭时,fclose()的返回值为0,如果返回值非零则表示有错误发生。

fgetc和fputc两个函数的用法

在C语言中读写文件很灵活,以字符形式读写文件时每次可以从文件中读取一个字符,或者向文件中写入一个字符

字符读取函数fgetc

fgetc是file get char 的缩写,意思是从文件中读取一个字符。fgetc()的用法为:

int fgetc (FILE *fp);

fp为文件指针。fgetc()读取成功返回读取到的字符,如若已经到了文件末尾或者读取失败时返回EOF

EOF是end of file 的缩写,表示文件末尾,实在stdio.h中定义的宏,它的值是一个负数(这个负数的值不定,看编译器的实现,但一定是负数),往往是-1。fgetc的返回值类型之所以为int,就是为了容纳这个负数(char不能为负数)。

在文件内部都有一个位置指针,用来指向当前的读写位置,也就是读写到了第几个字节,在文件打开时,该指针指向文件的第一个字节。使用fgetc()函数后,该指针会向后移动一个字节,所以可以连续多次使用fgetc()读取多个字符。

注意:这个文件内部的位置指针和C语言中的指针不是一回事。位置指针仅仅是一个标志,表示文件读写到的位置,也就是读写到第几个字节,它不表示地址。文件每读写一次,位置指针就会移动一次,它不需要你在程序中定义和赋值,而是由系统自动设置,对用户是隐藏的。

#include <stdio.h>
#include <errno.h>
int main(){
  FILE *fp;
  char ch;
  //文件如果读取错误就退出程序
  if((fp = fopen("D:\\demo.txt", "rt") == NULL)){
    perror("");
    exit(1);
  }
  //每次读取一个字节,直到读取完毕
  while((ch = fgetc(fp)) != EOF)
  putchar(ch);//输出所读到的字符
  putchar('\n');
  fclose(fp);//关闭文件
  return 0;
}

对EOF的说明

EOF本来表示文件末尾,意味着读取结束,但是很多函数在读取出错时也会返回EOF,那么当返回EOF时怎样判断这个文件是走到了文件末尾,还是中途出错。这里stdio.h输入输出头文件中给出了两个函数来解决这个问题:

  • feof()函数用来判断文件内部指针是否指向了文件尾部(当文件指向尾部时返回非0值,否则返回0值)
int feof(FILE *fp)
  • ferror()函数用来判断文件操作是否出错(如果出错返回非零值,没有出错返回0值)
int ferror(FILE *fp)

如果我们追求完美,防止上面的程序都不到结尾则我们可以在程序中加入这两个函数任意一个函数都能检查出文件是否被输出完:

#include <stdio.h>
int main(){
  FILE *fp;
  char ch;
  //文件如果读取错误就退出程序
  if((fp = fopen("D:\\demo.txt", "rt") == NULL)){
    printf("Fail to open file!\n");
    exit(0);
  }
  //每次读取一个字节,直到读取完毕
  while((ch = fgetc(fp)) != EOF)
  putchar(ch);//输出所读到的字符
  putchar('\n');
  if(ferror(fp))
    puts("读取错误");
  else
    puts("读取成功");
  fclose(fp);//关闭文件
  return 0;
}

字符写入函数fputc

fputc 是 file output char,简单的解释就是向指定的文件输入一个字符。

int fputc(int ch, FILE *fp)

ch是要输入的字符,fp是文件指针,fputs函数写入成功时返回写入的字符,写入错误时返回EOF。

fputs函数有两点说明:

  • 被写入的文件可以用写、读写、追加方式打开,用写或读写的方式打开一个已存在的文件时将清理原有文件的内容,并将写入的字符放在文件开头。如果需要保留文件的内容,并把写入的字符放在文件末尾,就必须以追加的方式打开文件,不管以何种方式打开,被写入的文件若不存在时则创建该文件。
  • 每写一个字符,文件内部位置指针向后移动一个字节。
#include <stdio.h>
int main(){
  FILE *fp;
  char ch;
  //判断文件是否被打开
  if((fp = fopen("D:\\demo.txt", "w+")) == NULL){//以写入/更新的方式读取文件。
    puts("Fail to open file!");
    exit(0);
  }
  puts("Input a string:");
  while((ch = getchar(ch) != '\n')
    fputc(ch, fp);
  fclose(fp);
  return 0;
}

fgets和fputs函数

fgetc、fputc只能向文件读取或者写入一个字符,fgets、fputs是向文件读取或写入一个字符串。

读字符串函数fgets

fgets()函数用来读取文件中的一串字符,并保存到字符数组中

char *fgets(char *str, int n, FILE *fp)

str为字符数组,n指的是要读取多少个字符,fp为文件指针

返回值,读取成功返回字符数组首地址,也即str;读取失败时返回NULL;如果开始读取时就在文件指针就指向了文件末尾,那么将读取不到任何数据也返回NULL;

注意:读取到的字符串会在字符串末尾默认加上’\0’,也就是说,我们需要读取n-1个字符,当我们需要读取100个字符时要将n的值设置成101,不然他只能读取99个字符。

还有一点,当读取到n-1字符之前如果出现了换行,或者读到了文件末尾,则读取结束。这就意味着,不管n值多大,fgets()只能读取一行的数据,不能跨行。在C语言中,没有按行读取文件的函数,我们可以借助fgets(),将n设置的足够大,每次就可以读取一行数据。

#include <stdio.h>
#define N 100

int main(){
  FILE *fp;
  char str[N+1];
  if((fp = fopen("D:\\demo.txt", "r")) == NULL){
    puts("Fail to open file!");
    exit(0);
  }
  while(fgets(str, N, fp) != NULL){
    puts(str);
  }
  fclose(fp);
  return 0;
}

如果我们有demo.txt文件,那么我们将输出它的每一行,当然输出一行的字符不超过一百个字符,只要每一行都不超过一百个字符,那么我们将输出这个文件中的所有内容。

fgets在读取文件时遇到换行符会一起读取到字符串中,而gets并不会它会将换行符舍弃。

写字符串函数fputs

fputs()函数是来向指定的文件写入一个字符串。

int fputs(char *str, FILE *fp);

str为要写入的字符串,fp为文件指针,写入成功返回非负值,失败返回EOF。

#include <stdio.h>
int main(){
  FILE *fp;
  char str[102] ={0}, strTemp[100];
  if((fp = fopen("D:\\demo.txt", "a+") ) == NULL){
    puts("Fail to open file!");
    exit(0);
  }
  puts("Input a string:");
  gets(strTemp);
  strcat(str,"\n");
  strcat(str,strTemp);
  fputs(str,fp);
  fclose(fp);
  return 0;
}

Posts in this Series