c的标准IO

文件的概念

  • 所谓文件是指一组相关数据的有序集合,这个数据集有一个名称,叫做文件名。如源程序文件,目标文件,可执行文件,头文件等

  • 文件通常时驻留在外部介质(如磁盘等)上的,在使用时才调入内存中来

  • 从用户角度看,文件可分为普通文件和设备文件两种

    • 普通文件:普通文件是指驻留在磁盘或其他外部介质上的一个有序数据集,可以是源文件,目标文件,可执行程序等

    • 设备文件:设备文件是指与主机相联的各种外部设备,如显示器,打印机,键盘等。在操作系统中,把外部设备也看作是一个文件来管理,把它们的输入,输出等同于磁盘文件的读和写

  • 从文件编码的形式看,文件可分为文本文件和二进制文件两种

    • 文本文件

      • ASCII码格式存放,一个字节放一个字符。文本文件的每一个字节存放一个ASCII码,代表一个字符。这便于对字符的逐个处理,但占用存储空间较多,转换为二进制速度慢,但直观易记。

      • 如整数 1000 => 0011000100110000001100000011000000110000

    • 二进制文件

      • 以补码形式存放。二进制文件是把数据以二进制数的格式存放在文件中的,其占用存储空间极少,无需转换。

      • 数据按其内存中的存储形式原样存放。一个字节不对应一个字符,故不能直接输出其字符样式。

      • 如:整数 1000 => 0010011100010000


文件系统的分类

目前c语言所使用的磁盘文件系统分为缓冲文件系统和非缓冲文件系统

  • 缓冲文件系统

    • 系统自动地在内存区为每一个正在使用的文件开辟一个缓冲区。从磁盘向内存读入数据时,则一次从磁盘文件将一些数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送给变量。向磁盘文件输出数据时,先将数据送到内存中的缓冲区,装满缓冲区后才一起送到磁盘去

    • 用缓冲区可以一次读入一批数据,或输出一批数据,而不是执行一次输入或输出函数就去访问一次磁盘,这样做的目的是减少磁盘的实际读写次数,提高读写的效率。

  • 非缓冲的文件系统

    • 不是有系统自动设置缓冲区,而由用户自己根据需要设置

    • 用户在程序中为每个文件设定缓冲区

  • 在传统的UNIX文件系统下,用缓冲文件文件系统来处理文本文件,用非缓冲文件系统处理二进制文件

  • 1983年ANSI C标准决定不采用非缓冲文件系统,而只采用缓冲文件系统。即用缓冲文件系统处理文本文件,也用它来处理二进制文件。也就是将缓冲文件系统扩充为可以处理二进制文件。

  • 一般把缓冲文件系统的输入输出称为标准输入输出(标准IO),非缓冲文件系统的输入输出称为系统输入输出(系统IO)。

  • 在c语言中没有输入输出语句,对文件的读写都是用库函数来实现的。ANSI C规定了标准输入输出函数,用它们对文件进行读写,需要使用头文件stdio.h


文件流的概念

  • 在c语言对文件的操作最终转化为对缓存中的数据进行操作,流实际上就是内存中的一种具有自动顺序操作的特殊数据,流如同流动的河水,有它的源和目的地。

  • 流根据方向可分为输入流和输出流

    • 输入流

      • 从文件读取数据的流

    • 输出流

      • 将数据输出到文件的流

  • 流根据数据内容可分为文本流和二进制流

    • 文本流

      • 二进制流就是流动着的字符序列

    • 二进制流

      • 二进制流就是流动着的二进制序列

  • 三个标准流

    • 标准输入流stdin(用数字0表示):针对标准输入,键盘

    • 标准输出流stdout(用数字1表示):针对标准输出,屏幕

    • 标准错误流stderr(用数字2表示):针对标准输出,屏幕

  • 上述所有的流都统称为文件流


文件类型结构体FILE和文件指针

  • 文件类型结构体FILE

    • 缓冲文件系统为每个正使用的文件在内存开辟文件信息区

    • 文件信息用系统定义的名为FILE的结构体描述

    • FILE定义在stdio.h

  • 文件指针的定义
    FILE *指针变量名

    • 文件指针实际上是一个文件类型结构体指针

    • 通过文件指针即可找到存放某个文件信息的文件类型结构体变量,然后按结构体变量提供的信息找到该文件,实施对文件的操作

    • 习惯上也笼统地把文件指针称为指向一个文件的指针或流指针

    • 当文件打开时,系统会自动创建文件类型结构体变量,并把指向它的指针返回回来,程序通过这个指针获得文件信息并访问文件

    • 文件关闭后,文件指针指向的结构体变量会被释放


文件打开

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

  • 功能:按指定方式打开文件

  • 参数

    • filename:为打开的文件路径(相对路径或绝对路径)

    • mode:使用文件的方式

  • 返回:正常打开,返回文件指针;打开失败,返回NULL

  • 标准输入,标准输出和标准错误是由系统打开的,可直接使用

  • 示例

FILE *fp;
fp =fopen("test.txt","w");

使用文件的方式

文件使用方式含义
rt只读打开一个文本文件,只允许读数据
wt只写打开或建立一个文本文件,只允许写数据
at追加打开一个文本文件,并在末尾写数据
rb只读打开一个二进制文件,只允许写数据
wb只写打开或建立一个二进制文件,只允许写数据
ab追加打开一个二进制文件,并在末尾写数据
rt+读写打开一个文本文件,允许读和写
wt+读写打开或建立一个文本文件,允许读和写
at+读写打开一个文本文件,允许读或在末尾追加数据
rb+读写打开一个二进制文件,允许读和写
wb+读写打开或建立一个二进制文件,允许读和写
ab+读写打开一个二进制文件,允许读或在末尾追加数据

符号说明

  • r(read):读

  • w(write):写

  • a(append):追加

  • t(text):文本文件,可以省略不写

  • b(binary):二进制文件

  • +:读和写

文件使用的处理方式

mode处理方式当文件不存在当文件存在写文件读文件
r读取出错打开文件不能
w写入建立新文件覆盖原有文件不能
a追加建立新文件在原有文件后追加不能
r+读取/写入出错打开文件
w+读取/写入建立新文件覆盖原有文件
a+读取/写入建立新文件在原有文件后追加

如果是二进制文件,在使用时只要在模式后添加字符b即可,如rb,rb+分别表示读取二进制文件和以读取/写入方式打开二进制文件


文件关闭

int fclose(FILE *fp);

  • 功能:关闭fp指向的文件,释放文件类型结构体和文件指针。

  • 参数:fp打开文件时返回的文件指针

  • 返回:成功返回0,失败返回-1

  • 注意点:不关闭文件可能会丢失数据

    • 向文件写数据时,是先将数据输出到缓冲区,待缓冲区充满后才正式输出给文件。如果当数据未充满缓冲区而程序结束允许,就会将缓冲区的数据丢失

    • fclose先把缓冲区的数据输出到磁盘文件(刷新缓存),然后才会释放文件类型结构体和文件指针


测试文件读写位置

int ftell(FILE *fp);

  • 功能:测试当前文件的读写位置

  • 返回:测试成功返回文件位置指针所在的位置(当前读写位置距离文件开头的字节数),失败则返回-1


常用的标准IO函数

getchar

int getchar();

  • 功能:从标准输入读取一个字符

  • 返回:成功返回读取的字符,否则返回EOF

putchar

int putchar();

  • 功能:将一字符ch写入到标准输出

fgetc

int fgetc(FILE *fp);

  • 功能:从fp指向的文件中读取一个字符

  • 返回:成功返回读取的字符,否则返回EOF(-1)

  • 可针对标准输入操作

fputc

int fputc(int ch,FILE *fp);

  • 功能:把一字符ch写入fp指向的文件中

  • 返回:成功返回写入的字符ch,失败返回EOF

  • 可针对标准输入输出操作

ungetc

int ungetc(int c,FILE *fp);

  • 功能:撤销一个字符

fgets

char *fgets(char *str,int siez,FILE *fp);

  • 功能:从fp指向的文件中至多读size - 1个字符,放到str指向的字符数组中,如果在读入size - 1个字符结束前遇到换行符或EOF,读入即结束,字符串读入后在最后加一个\0

  • 返回:成功返回str字符串指针,失败返回NULL

  • 可针对标准输入操作

fputs

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

  • 功能:把str指向的字符串或字符数组写入fp指向的文件中

  • 返回:成功返回0,出错返回EOF

  • 可针对标准输出操作

fscanf和fprintf

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

  • 功能:按format格式对fp指向的文件进行IO操作

  • 返回:成功返回IO字节数,失败或到文件末尾返回EOF

  • 可针对标准输入和输出操作

  • 示例

#include <stdio.h>
#include <string.h>
int main(int argc,char *argv[])
{
        if(argc < 2)
  6         {
                fprintf(stdout,"parameter error\n");
                return 1;
        }   
        FILE *fp1 = fopen("/etc/passwd","r");
        FILE *fp2 = fopen(argv[1],"r");
        char str[1024] = {'\0'},name[20] = {'\0'},passwd[20] = {'\0'},description[20] = {'\0'},home[20] = {'\0'},bash[20] = {'\0'}; 
        int uid = 0,gid = 0;
        // 这里读取之后,整行的内容都在name中
        int flag = fscanf(fp1,"%[^:]:%[^:]:%d:%d:%[^:]:%[^:]:%[^\n]\n",name,passwd,&uid,&gid,description,home,bash); 
        while((flag = fscanf(fp1,"%[^:]:%[^:]:%d:%d:%[^:]:%[^:]:%[^\n]\n",name,passwd,&uid,&gid,description,home,bash))!= EOF)
        {   
                printf("%d\n",flag);                fprintf(stdout,"name=%s;passwd=%s;uid=%d;gid=%d;description=%s;home=%s;bash=%s\n",name,passwd,uid,gid,description,home,bash);
                // description位置可能会为空,为空的话,文件指针的位置不会移动到下一行,这个需要处理下
                if(0 == flag)
                {   
                        fscanf(fp1,"%*[^\n]\n");
                        // 清空description的数据
                        memset(description,'\0',sizeof(description));
                        //break;
                }           
        }           
        return 0;   
}

sscanf

int sscanf(char const *str,char const *format,...);
int sprintf(char const *str,char const *format,...);
  • 功能:按format格式对str指向的字符数组进行IO操作

  • 返回:成功返回I/O字节数,失败返回EOF

  • 实例

#include <stdio.h>
int main(void)
{
        char str1[20] = "666 jin";
        int id = 0;
        char name[10] = {'\0'};
        // 这里不字符串666转换成了数字
        sscanf(str1,"%d %s",&id,name);
        printf("id = %d,name = %s\n",id,name);
        // 不数字666转换成字符串666
        char number[5] = {'\0'};
        sprintf(number,"%d",id);
        printf("number = %s\n",number);
        return 0;
}

fread和fwrite

int fread(void *buffer,int num_bytes,int count,FILE *fp);
int fwrite(void *buffer,int num_bytes,int count,FILE *fp);
  • 功能:读写数据块,一般用于二进制文件的输入输出

  • 返回:成功返回读写的元素个数,失败或到文件末尾返回EOF

  • 参数:

    • buffer:一个指向要进行输入输出数据存储区的通用指针

    • num_bytes:每个要读写的元素的字节数

    • count:要读写的元素个数

    • fp:要读写的文件指针

  • fread和fwrite函数的使用注意点

    • fprintffscanf函数对磁盘读写,使用方便,容易理解,但由于在输入时要将ASCII码转换成二进制形式,在输出时又要将二进制形式转换成字符,花费时间较多。

    • 因此,在内存与磁盘频繁交换数据的情况下,最好不要用fprintffscanf函数,而用freadfwrite函数

  • 实例

#include <stdio.h>
struct User
{
        int id;
        char name[20];
};
int main(int argc,char *argv[])
{
        if(argc < 2)
        {
                printf("parameter error\n");
                return 1;
        }
        FILE *fp1 = fopen("/etc/passwd","r");
        FILE *fp2 = fopen(argv[1],"wb");
        if(fp1 == NULL || fp2 == NULL)
        {       
                printf("parameter error\n");
                return 1;
        }
        struct User user;
        while(EOF != fscanf(fp1,"%[^:]:%*[^:]:%d:%*[^\n]\n",user.name,&user.id))
        {
                if(EOF == fwrite(&user,sizeof(struct User),1,fp2))
                {
                        printf("fail to write file\n");
                        return 1;
                }
                //printf("uid = %i;name = %s\n",user.id,user.name);
        }
        fclose(fp1);
        fclose(fp2);
        fp2 = fopen(argv[1],"rb");
        /* 利用返回值判断是否到达文件尾
        while(0 != fread(&user,sizeof(struct User),1,fp2))
        {
                printf("uid = %i;name = %s\n",user.id,user.name);
        }
        */
        fread(&user,sizeof(struct User),1,fp2);
        // 使用feof判断是否是文件结尾
        while(!feof(fp2))
        {
                printf("uid = %i;name = %s\n",user.id,user.name);
                fread(&user,sizeof(struct User),1,fp2);
        }

        return 0;
}

fseek函数

int fseek(FILE *fp,long offset,int whence);

  • 功能:使fp所指文件的位置指针重置到指定位置(从whence位置移动offset个字节)

  • 返回:成功返回0,失败返回-1

  • offset:位移量,表示移动的字节数

  • whence:

    • SEEK_SET:文件首 0 offset非负

    • SEEK_CUR:文件当前读写位置 1 offset可正可负

    • SEEK_END:文件尾 2 offset可正可负

rewind函数

void rewind(FILE *fp);

  • 功能:使文件位置指针重新返回文件首

remove函数

int remove(const char *filename);

  • 功能:删除指定的文件

  • 返回:成功返回0,失败返回-1

fflush函数

void fflush(FILE *fp);

  • 功能:刷新缓冲区。如果打开文件进行读操作,该函数将清空文件的输入输出缓冲区,如果打开文件进行写操作时,该函数将文件的输出缓冲区内容写入文件中

标签: c, c的标准io

添加新评论