C和C++的文件操作

1、C的文件操作

    1.1 不带缓冲I/O

    1.2 标准I/O库

    1.3 不带缓冲I/O和标准I/O的区别

2、C++的文件操作


1、C的文件操作

1.1 不带缓冲I/O

    不带缓冲的I/O每次调用都会触发内核中的一个系统调用。这类I/O操作是围绕一个文件的文件描述符来进行的。此类文件操作常用的函数如下表,这些函数及其所用的一些符号在io.h和fcntl.h中定义,在使用时要加入相应的头文件。

函数原型功能介绍
int open(const char *path, int access [, unsigned mode]);

   打开一个文件并返回它的文件描述符,如果失败,将返回一个小于0的值。参数path是要打开的文件名,access是打开的模式,mode是可选项。access可取的值如下:

  O_RDONLY :只读方式

  O_WRONLY :只写方式

  O_RDWR :读/写方式 
  O_NDELAY :用于UNIX系统

  O_APPEND :追加方式

  O_CREAT :如果文件不存在就创建 
  O_TRUNC :把文件长度截为0

  O_EXCL 和O_CREAT连用,如果文件存在返回错误

  O_BINARY :二进制方式 
  O_TEXT :文本方式

对于多个要求,可以用"|"运算符来连接

int close(int handle);关闭一个文件描述符。
long lseek(int handle, long offset, int fromwhere);定位到指定的位置,参数offset是移动的量,fromwhere是移动的基准位置,SEEK_SET:文件首部;SEEK_CUR:文件当前位置;SEEK_END:文件尾。
int read(int handle, void *buf, unsigned len);从文件读取一块,参数buf保存读出的数据,len是读取的字节。函数返回实际读出的字节。
int write(int handle, void *buf, unsigned len);写一块数据到文件中,参数的含义同read(),返回实际写入的字节。
int eof(int handle);测试文件是否结束,是返回1,否则返回0;
long filelength(int handle);返回文件长度
int rename(const char *oldname, const char *newname); 重命名文件,参数oldname是旧文件名,newname是新文件名。成功返回0
int chsize(int handle, long size);改变文件长度,参数size表示文件新的长度,成功返回0,否则返回-1,如果指定的长度小于文件长度,则文件被截短;如果指定的长度大于文件长度,则在文件后面补'/0'。

1.2 标准I/O库

    这种方式的文件操作有一个重要的结构流——FILE,FILE在stdio.h中定义如下:

typedef struct { 
  int level;           /* fill/empty level of buffer */ 
  unsigned flags;        /* File status flags */ 
  char fd;            /* File descriptor */ 
  unsigned char hold;      /* Ungetc char if no buffer */ 
  int bsize;           /* Buffer size */ 
  unsigned char _FAR *buffer; /* Data transfer buffer */ 
  unsigned char _FAR *curp;  /* Current active pointer */ 
  unsigned istemp;      /* Temporary file indicator */ 
  short token;          /* Used for validity checking */ 
} FILE;              /* This is the FILE object */ 

    标准I/O库的函数如下:    

函数原型功能介绍
FILE *fopen(const char *filename,const char *mode);

  fopen实现三个功能:为使用而打开一个流、把一个文件和此流相连接、给此流返回一个FILE指针 。参数filename指向要打开的文件名,mode表示打开状态的字符串,其取值如下表:

  "r" :以只读方式打开文件 
  "w" :以只写方式打开文件 
  "a" :以追加方式打开文件 
  "r+" :以读/写方式打开文件,如无文件出错 
  "w+" :以读/写方式打开文件,如无文件生成新文件

int fclose(FILE *fp);关闭用fopen()打开的文件,如果成功,返回0,失败返回EOF。
int fputc(int c, FILE *stream); 向流写一个字符,成功返回这个字符,失败返回EOF。
int fputc(FILE *stream);从流中读一个字符,成功返回这个字符,失败返回EOF。
int fseek(FILE *stream, long offset, int whence);

  定位到流中指定的位置,如果成功返回0,参数offset是移动的字符数,whence是移动的基准,取值是:

  SEEK_SET 0 文件开头 
  SEEK_CUR 1 当前读写的位置 
  SEEK_END 2 文件尾部

int fputs(const char *s, FILE *stream);写一个字符串到流中
char *fgets(char *s, int n, FILE *stream); 从流中读取n-1个字符,除非读完一行,参数s是来接收字符串,如果成功则返回s的指针,否则返回NULL。
int fprintf(FILE *stream, const char *format[, argument, ...]);按格式输入到流,其用法和printf()相同,不过不是写到控制台,而是写到流。
int fscanf(FILE *stream, const char *format[, address, ...]);从流中按格式读取,其用法和scanf()相同,不过不是从控制台读取,而是从流读取。
int feof(FILE *stream);检测是否已到文件尾,是返回真,否则返回0。
int ferror(FILE *stream);返回流最近的错误代码
void rewind(FILE *stream);把当前的读写位置回到文件开始。
int remove(const char *filename); 删除文件,参数就是要删除的文件名,成功返回0。
size_t fread(void *ptr, size_t size, size_t n, FILE *stream);从流中读指定个数的字符,参数ptr是保存读取的数据,void*的指针可用任何类型的指针来替换,如char*、int *等等来替换;size是每块的字节数;n是读取的块数,如果成功,返回实际读取的块数(不是字节数),本函数一般用于二进制模式打开的文件中。
size_t fwrite(const void *ptr, size_t size, size_t n, FILE *stream);向流中写指定的数据,参数ptr是要写入的数据指针,void*的指针可用任何类型的指针来替换,如char*、int *等等来替换;size是每块的字节数;n是要写的块数,如果成功,返回实际写入的块数(不是字节数),本函数一般用于二进制模式打开的文件中。
FILE *tmpfile(void); 生成一个临时文件,以"w+b"的模式打开,并返回这个临时流的指针,如果失败返回NULL。在程序结束时,这个文件会被自动删除。
char *tmpnam(char *s); 生成一个唯一的文件名,其实tmpfile()就调用了此函数,参数s用来保存得到的文件名,并返回这个指针,如果失败,返回NULL。

1.3 不带缓冲I/O和标准I/O的区别

     这里使用两个对应的函数进行比较:

  • ssize_t write(int filedes, const void *buff, size_t nbytes);
  • size_t fwrite(const void *ptr, size_t size, size_t nobj, FILE *fp);

    上面的buff和ptr都是指应用程序自己使用的buffer,实际上当需要对文件进行写操作时,都会先写到内核所设的缓冲存储器。如果该缓存未满,则并不将其排入输出队列,直到缓存写满或者内核再次需要重新使用此缓存时才将其排入磁盘I/O输入队列,再进行实际的I/O操作,也就是此时才把数据真正写到磁盘,这种技术叫延迟写。

    如果我们直接用不带缓冲/O对内核的缓冲区进行读写,会产生许多管理不善而造成的麻烦(如一次性写入过多,或多次系统调用导致的效率低下)。标准I/O为我们解决了这些问题,它处理很多细节,如缓冲区分配,以优化长度执行I/O等,更便于我们使用。由于标准I/O在系统调用的上一层多加了一个缓冲区,也因此引入了流的概念,在UNIX/Linux下表示为FILE*(并不限于 UNIX/Linux,ANSI C都有FILE的概念),FILE实际上包含了为管理流所需要的所有信息:实际I/O的文件描述符,指向流缓存的指针(标准I/O缓存,由malloc分 配,又称为用户态进程空间的缓存,区别于内核所设的缓存),缓存长度,当前在缓存中的字节数,出错标志等。因此可知,不带缓存的I/O对文件描述符操作,带缓存的标准I/O是针对流的。标准I/O对每个I/O流自动进行缓存管理(标准I/O函数通常调用malloc来分配缓存)。它提供了三种类型的缓存:

  • 全缓存:当填满标准I/O缓存后才执行I/O操作。磁盘上的文件通常是全缓存的。
  • 行缓存:当输入输出遇到新行符或缓存满时,才由标准I/O库执行实际I/O操作。stdin、stdout通常是行缓存的。
  • 无缓存:相当于read、write了。stderr通常是无缓存的,因为它必须尽快输出。

    一般而言,由系统选择缓存的长度,并自动分配。标准I/O库在关闭流的时候自动释放缓存。另外,也可以使用函数fflush()将流所有未写的数据送入(刷新)到内核(内核缓冲区),fsync()将所有内核缓冲区的数据写到文件(磁盘)。

    在标准I/O库中也有引入缓存管理而带来的缺点--效率问题。例如当使用每次一行函数fgets和fputs时,通常需要复制两次数据:

  • 一次是在内核和标准I/O缓存之间(当调用read和write时);
  • 第二次是在标准I/O缓存(通常系统分配和管理)和用户程序中的行缓存(fgets的参数就需要一个用户行缓存指针之间。 

2、C++的文件操作

    在C++中,有一个stream这个类,所有的I/O都以这个"流"类为基础的,包括我们要认识的文件I/O,stream这个类有两个重要的运算符:

  • 插入器(<<) :向流输出数据。比如说系统有一个默认的标准输出流(cout),一般情况下就是指的显示器,所以,cout<<"Write Stdout"<<'/n';就表示把字符串"Write Stdout"和换行字符('/n')输出到标准输出流。
  • 析取器(>>) :从流中输入数据。比如说系统有一个默认的标准输入流(cin),一般情况下就是指的键盘,所以,cin>>x;就表示从标准输入流中读取一个指定类型(即变量x的类型)的数据。

    在C++中,对文件的操作是通过stream的子类fstream(file stream)来实现的,所以,要用这种方式操作文件,就必须加入头文件fstream.h。

            

    接下来我们介绍fstream类中的成员函数:

成员函数原型函数功能介绍
void open(const char* filename,int mode,int access);  打开文件,filename是要打开的文件名,mode是要打开文件的方式,access是打开文件的属性 。打开文件的方式在类ios(是所有流式I/O类的基类)中定义,常用的值如下:

  ios::app:以追加的方式打开文件 
  ios::ate:文件打开后定位到文件尾,ios:app就包含有此属性 
  ios::binary:以二进制方式打开文件,缺省的方式是文本方式。
  ios::in: 文件以输入方式打开 
  ios::out: 文件以输出方式打开 
  ios::nocreate: 不建立文件,所以文件不存在时打开失败  
  ios::noreplace:不覆盖文件,所以打开文件时如果文件存在失败 
  ios::trunc:  如果文件存在,把文件长度设为0 
  可以用"或"把以上属性连接起来,如ios::out|ios::binary

打开文件的属性取值是:

  0:普通文件,打开访问 
  1:只读文件 
  2:隐含文件 
  4:系统文件 
  可以用"或"或者"+"把以上属性连接起来 ,如3或1|2就是以只读和隐含属性打开文件。

close();打开的文件使用完成后一定要关闭,fstream提供了成员函数close()来完成此操作。
ofstream &put(char ch);读写文件分为文本文件和二进制文件的读取,对于文本文件的读取比较简单,用插入器和析取器就可以了。而对于二进制文件,put()函数向流写入一个字符。
ifstream &get(char &ch);从流中读取一个字符,结果保存在引用ch中,如果到文件尾,返回空字符。
int read(unsigned char *buf,int num); 读二进制数据块
int write(const unsigned char *buf,int num);写二进制数据块
getline(char *str,  streamsize num);读取一行到一个字符数组中,str为C字符数组首地址,num为需要读取的字符个数,而不是字节个数。如果单行超过了缓冲,则循环会结束。用getline的时候,一定要保证缓冲区够大,能够容纳各种可能的数据行。推荐使用全局函数getline(istream&, string&)代替成员函数getline。
int eof();检测是否到达文件尾,如果到达文件尾返回非0值,否则返回0。
istream &seekg(streamoff offset,seek_dir origin); 文件定位 ,设置读位置
ostream &seekp(streamoff offset,seek_dir origin);文件定位 ,设置写位置

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值