C语言文件

[TOC]

UNIX的哲学,万物皆文件.

打开关闭文件

FILE * fopen(const char *filename,const char * type);

系统调用fopen()打开文件:给用户指定的文件在内存中分配一个FILE结构,并将结构返回给用户程序,以后用户就可以更具FILE指针来实现对文件的存取操作了.当使用打开函数时必须给出文件名和操作方式.如果文件名不存在,就意味着创建文件,并将FILE *指针指向该文件.如果存在就意味着删除该文件.

FILE 结构体如下

typedef struct __sFILE {
    unsigned char *_p;  /* current position in (some) buffer */
    int _r;     /* read space left for getc() */
    int _w;     /* write space left for putc() */
    short   _flags;     /* flags, below; this FILE is free if 0 */
    short   _file;      /* fileno, if Unix descriptor, else -1 */
    struct  __sbuf _bf; /* the buffer (at least 1 byte, if !NULL) */
    int _lbfsize;   /* 0 or -_bf._size, for inline putc */

    /* operations */
    void    *_cookie;   /* cookie passed to io functions */
    int (* _Nullable _close)(void *);
    int (* _Nullable _read) (void *, char *, int);
    fpos_t  (* _Nullable _seek) (void *, fpos_t, int);
    int (* _Nullable _write)(void *, const char *, int);

    /* separate buffer for long sequences of ungetc() */
    struct  __sbuf _ub; /* ungetc buffer */
    struct __sFILEX *_extra; /* additions to FILE to not break ABI */
    int _ur;        /* saved _r when _r is counting ungetc data */

    /* tricks to meet minimum requirements even when malloc() fails */
    unsigned char _ubuf[3]; /* guarantee an ungetc() buffer */
    unsigned char _nbuf[1]; /* guarantee a getc() buffer */

    /* separate buffer for fgetln() when line crosses buffer boundary */
    struct  __sbuf _lb; /* buffer for fgetln() */

    /* Unix stdio files get aligned to block boundaries on fseek() */
    int _blksize;   /* stat.st_blksize (may be != _bf._size) */
    fpos_t  _offset;    /* current lseek offset (see WARNING) */
} FILE;

1.对于mode有常见的方式

mode description
r (read) 以只读方式打开文件.该文件必须存在;
w (write) 只写的方式.若文件存在原有的内容会被清除;若文件不存在,创建文件;
a (append) 追加方式打开只写文件,只允许进行写操作.文件存在,则添加内容在文件末尾;文件不存在则创建文件 (EOF符保留)
+ (read and write) 可读可写
b (binary) 以二进制方式打开文件
t (text) 以文本方式打开文件(默认以该模式打开文件)

2.常见的组合方式

composed mode description
r+ 以读写的方式操作文件,允许读写.文件必须存在,否则返回NULL.打开成功返回指向文件的指针,指向文件的头部 (注意很多书上或资料上讲述追加方式打开成功后位置指针指向文件末尾是错误的);
rb+ 以可读可写,二进制方式打开文件,允许读写.文件必须存在,否则返回NULL.若打开文件成功返回文件指针,指向文件头部;
rt+ 以可读可写,文本方式打开文件,允许读写.文件必须存在,否则返回NULL.若打开文件成功,返回文件指针,指向文件头部;
w 以只写的方式打开文件,只允许写,若文件存在,文件中原有内容会被清除;若文件不存在,则创建文件,打开成功后返回文件指针,位置指针指向文件头部
w+ 以读写的方式打开文件,允许读写,若文件存在,文件中原有内容会被清除;若文件不存在,则创建文件,打开成功后返回文件指针,位置指针指向文件头部
a 以追加只写的方式打开文件,只允许写.若文件存在,则追加的内容在文件的末尾,若文件不存在则创建文件.打开成功后返回文件的指针,指向文件的头部.
a+ 以追加、可读写的方式打开文件,允许读写。若进行读操作,则从头开始读;若进行写操作,则将内容添加在末尾。若文件不存在,则创建文件。打开成功后返回文件指针,位置指针指向文件头部(不保留EOF)

二进制和文本打开方式基本相同,不同的地方是读取文本是碰到ASCII码为26的字符是,则会停止文件的读写,默认文件以及结束.应为正常的文本不会有ASCII码26的字符.而二进制打开方式不存在这个问题.(UNIX下没有区别);

注意:

1)在以追加方式打开文件时,位置指针指向文件的首部。

​ 在这里区分一下位置指针和文件指针:

​ 文件指针:指向存储文件信息的一个结构体的指针

​ 位置指针:对文件进行读写操作时移动的指针

​ 在头文件<stdio.h>中存在一个结构体_iobuf,在VC6.0中选中FILE,然后F12,则可以看到_iobuf的具体定义(UNIX 的大致相同):

struct _iobuf
{
        char *_ptr;               // 指向buffer中第一个未读的字节       
        int   _cnt;                 // 记录剩余未读字节的个数
        char *_base;           // 指向一个字符数组,即这个文件的缓冲
        int   _flag;                // FILE结构所代表的打开文件的一些属性
        int   _file;                 // 用于获取文件描述,可以使用fileno函数获得此文件的句柄。
        int   _charbuf;          // 单字节的缓冲,即缓冲大小仅为1个字节,如果为单字节缓冲,_base将无效
        int   _bufsiz;            // 记录这个缓冲的大小
        char *_tmpfname;    // temporary file (i.e., one created by tmpfile()
                                        // call). delete, if necessary (don't have to on
                                        // Windows NT because it was done by the system when
                                        // the handle was closed). also, free up the heap
                                        // block holding the pathname.
};
typedef struct _iobuf FILE;

​ 比如用FILE *fp定义了一个文件指针,并成功打开一个文件之后,fp只是指向该结构体,而在对文件进行读写操作时,fp的值并不会改变,改变的是结构体中_ptr的值,这个_ptr就是位置指针。

​ 2)以追加方式打开时,若进行写操作,则rewind函数和fseek函数不会起到作用,因为以追加方式打开时进行写操作的话,系统会自动将位置指针移动到末尾。

​ 3)当文件打开用于更新时,可以通过文件指针对文件进行读写操作,但是如果没有给出fseek或者rewind的话,读操作后面不能直接跟写操作,否则会是无效的写操作(位置指针会移动,但是需要写入文件的内容不会被写入到文件当中),但是写操作后可以直接跟读操作。

每次调用fopen()打开文件后都要记得调用fclose()关闭文件

C语言提供了以下几种文件读写方式

1.字符读写: fgetc()/fputc() (读/写);

fgetc()函数:

(1)一般调用形式: char ch = fgetc(fd);

(2)作用: 文件中读取一个字符;

(3)返回值:

​ 成功:返回值所得到的字符;
​ 失败:返回EOF(-1)。

*注意问的打开方式

fputc()函数:

(1)一般调用形式: char res = fputc(fd,ch);

(2)作用: 文件中写入一个字符;

(3)返回值

​ 成功:函数输入的字符;
​ 失败:返回EOF(-1)。

说明:函数putchar()是在stdio.h中用预处理命令定义的宏,即:

//define putchar(c) fputc(c,stdout)

char a = 'a';
char ch = fgetc(fd);
char res = fputc(a, fd);

字符串读写: fgets()/fputs() (读/写);

1.fgets()函数:

  • 一般调用形式:char * fgets(char *str ,int num ,FILE *fd); (fd 的mode rt)

    参数说明: str 保存从文件中读取的字符串;
    ​ num 从文件中读取的字符串中字符个数不超过num-1.在读入最后一个字符后加上串结束标志'\0';

  • 返回值:
    成功: 返的字符串;

    失败: 返回NULL

2.fputs() 文件中写入字符串

  • 一般调用形式: int num = fputs(char *s,FILE *fd); (fd 的mode at+)

    s 要写入的字符串;

    fd 待写入的文件;

    返回值:

    ​ 成功: 写入的字符个数num;

    ​ 失败: EOF(-1);

int res = fputs("test", fd);
    
char rs[11];
char *r = fgets(rs, 11, fd);

数据块读写: fwrite()/fread() (读/写)

  • 一般调用形式:

    size_t fwrite(void *buffer,size_t size ,size_t count, FILE *fd);

    size_t fread(void *buffer,size_t size, size_t count ,FILE *fd);

  • 参数说明:

    buffer: 缓冲区指针.对fread,它是暂存读入数据的指针;对fwrite,它是要输出数据的指针;
    size: 要读写的字节数;
    count: 要进行读写多少个size字节的数据项;
    fd: 待读写的文件指针;

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

struct stu{
    int age;
    int num;
    char name[10];
    char addr[20];
}boya[2],boyb[2],*qq,*pp;
int main() {
    FILE *fd;
    char ch;
    int i;
    qq = boya;
    pp = boyb;
    if ((fd = fopen("/Users/sks/Desktop/stu_list", "wb+")) == NULL) {
        printf("Cannot open file. Strike any key to exit!");
        getchar();
        exit(1);
    }
    printf("intput data\n");
    for (i=0; i<2; i++,qq++) {
        scanf("%d%d%s%s",&qq->age,&qq->num,qq->name,qq->addr);
    }
    qq = boya;
    size_t writeSize = fwrite(qq, sizeof(struct stu), 2, fd);
    rewind(fd);
    size_t readSize = fread(pp, sizeof(struct stu), 2, fd);
    
    printf("\n\nname\tnumber      age      addr\n");
    for (i=0; i<2; i++,pp++) {
        printf("%s   %d   %d   %s\n",pp->name,pp->num,pp->age,pp->addr);
    }
    fclose(fd);
    return 0;
}

本例程序定义了一个结构stu,说明了两个结构数组boya和boyb以及两个结构指针变量pp和qq。pp指向boya,qq指向boyb。程序第14行以读写方式打开二进制文件“stu_list”,输入二个学生数据之后,写入该文件中,然后把文件内部位置指针移到文件首,读出两块学生数据后,在屏幕上显示。

格式化读写: fscanf()/fprintf() (写/读);

一般调用形式:

​ (1)int fprintf(FILE *stream,const char *format,[argument]…) 输出格式化字符串或者将格式化字符串输出到流 (文件);

​ (2)int fscanf(FILE *stream, const char *) 输入文件中的内容到某个变量中.

​ fscanf(fd,"%s",res)

返回值:

​ 成功:

​ fprintf读取的字符个数;

​ fscanf: 返回1

​ 失败:

​ EOF;

int res = fprintf(fd, "%s",s);
    if (res == EOF) {
        printf("输入失败\n");
    }
    
    char tmp[26];
//
   int len = fscanf(fd, "%s\n",tmp);

其他文件相关操作

ftell()函数: 得到流式文件的当前读写位置,返回流式文件当前读写位置距离文件头部的字节数.

long     ftell(FILE *);

fseek(): 把fd 的文件读写位置指针移动到知道的位置;

@param FILE* 待操作的文件指针
@param long  距离起始点的位置
@parma int   计算的起始点
@return  0 success 文件位置指针指向正确的offset,错误返回-1
/*
    有三类起始点
    SEEK_SET 0   文件开头
    SEEK_CUR 1   文件当前位置
    SEEK_END 2   文件末尾
*/
int  fseek(FILE *, long offset, int origin);

rewind(): 将文件位置指针重新指向一个文件流的开头

void     rewind(FILE *);

eg:

long fileLength = 0; 
fseek(fd, 0, SEEK_END);
fileLength = ftell(fd);
rewind(fd);

fflush():清空缓存

所谓flush一个缓冲,是指对写缓冲而言,将缓冲内的数据全部写入实际的文件,并将缓冲清空,这样可以保证文件处于最新的状态。之所以需要flush,是因为写缓冲使得文件处于一种不同步的状态,逻辑上一些数据已经写入了文件,但实际上这些数据仍然在缓冲中,如果此时程序意外地退出(发生异常或断电等),那么缓冲里的数据将没有机会写入文件。flush可以在一定程度上避免这样的情况发生。

在这个表中我们还能看到C语言支持两种缓冲,即行缓冲(Line Buffer)和全缓冲(Full Buffer)。全缓冲是经典的缓冲形式,除了用户手动调用fflush外,仅当缓冲满的时候,缓冲才会被自动flush掉。而行缓冲则比较特殊,这种缓冲仅用于文本文件,在输入输出遇到一个换行符时,缓冲就会被自动flush,因此叫行缓冲。

 FILE *fd;
    if ((fd = fopen("/Users/sks/Desktop/ch.txt", "w")) == NULL) {
        printf("Open file failed.Press any key to exit!");
        getchar();
        exit(1);
    }
    
    Student stu;
    stu.number = 10000;
//    stu.name = "guohuabing";
    strcpy(stu.name, "guohuabing");
    fflush(fd);
    fwrite(&stu, sizeof(struct SStudent), 1, fd);
    fclose(fd);

socket 通信和文件操作的关系

待续....

C语言文件操作和操作系统文件子系统的关系

参考:

http://www.2cto.com/kf/201207/143344.html

http://www.cnblogs.com/L-hq815/archive/2012/06/30/2571066.html

http://www.cnblogs.com/dolphin0520/archive/2011/10/05/2199598.html

http://c.biancheng.net/cpp/html/107.html

推荐阅读更多精彩内容

  • 语言中对文件进行操作必须首先打开文件,打开文件主要涉及到fopen函数。fopen函数的原型为 FILE* fop...
    朱森阅读 558评论 0 1
  • 文件指针 FILE * 指针变量标识符作用:通过该指针即可找到存放某个文件信息的结构变量,然后按结构变量提供的信息...
    永断阎罗阅读 323评论 0 3
  • 所谓“文件”是指一组相关数据的有序集合,该数据的集合的名字就是文件名。文件可以分为很多类,如源程序文件、目标文件、...
    一叶之界阅读 292评论 0 0
  • c语言里面的各种字符/字符串读写一直搞得我分不清楚。。今天来学习总结一下:原文章来自http://www.cnbl...
    AwesomeAshe阅读 318评论 0 0
  • Android NDK开发之旅 目录 文件读写 一个文件,无论它是文本文件还是二进制文件,都是代表了一系列的字节。...
    香沙小熊阅读 4,025评论 0 4
  • 吉木萨尔县大有镇桦窝村村民马建玲在养殖增收的道路上,善于把握市场需求巧养肉兔,年收入近5万元实现了依靠特色养殖致富...
    养殖先生阅读 152评论 0 2
  • ——江瀚伯懿 寂静的巷口有着古老的维也纳气息, 像法国的诗人浪漫而平静。 我在巷口、巷口在梦里...
    江瀚阅读 169评论 1 0