c 文件读写

读写模式

FILE 结构体

三种刷新缓冲区的操作

简单读写示例: fgetc 与 fputc

fgets 与 fputs

fread 与 fwrite

fscanf 与 fprintf

rewind 和 fseek 随机读写文件

feof

拷贝文件

read 与 write

错误集合

#include <stdio.h>
fclose() 的返回值为0,如果返回非零值则表示有错误发生

  • 文本模式和二进制模式的区别
    • windows
      • windows中fopen函数用文本模式写入文件时,会自动将\n换成\r\n,实现自动换行。
      • 用二进制模式时,写入文件的是\n,windows并不能识别到换行,因此出现了小方块。
    • linux系统中两种模式就没有区别

读写模式

模式 介绍
“r”(只读) 为输入打开一个文本文件 对文件进行读操作
“w”(只写) 为输出打开一个文本文件 对文件进行写操作
“a”(追加) 向文本文件尾添加数据
“rb”(只读) 为输入打开有一个二进制文件
“wb”(只写) 为输出打开一个二进制文件对文件进行写操作
“ab”(追加) 向二进制文件尾添加数据
“r+”(读写) 为读写打开一个文本文件
“w+”(读写) 为读写建立一个新的文本文件
“a+”(读写) 向文本文件尾添加数据
“rb+”(读写) 为读写打开一个二进制文件
“wb+”(读写) 为读写建立一个新的二进制文件
“ab+”(读写) 为读写打开一个进制文件
"t" 文本文件,如果不写默认"t"
  • 调用 fopen() 函数时必须指明读写权限,但是可以不指明读写方式(此时默认为"t")。
  • 读写权限和读写方式可以组合使用,但是必须将读写方式放在读写权限的中间或者尾部(换句话说,不能将读写方式放在读写权限的开头)。例如:
    • 将读写方式放在读写权限的末尾:"rb"、"wt"、"ab"、"r+b"、"w+t"、"a+t"
    • 将读写方式放在读写权限的中间:"rb+"、"wt+"、"ab+"

FILE 结构体

struct _iobuf {
        char *_ptr;   // 下一个要被读取的字符地址
        int   _cnt;    // 剩余的字符,如果是输入缓冲区,那就表示缓冲区中还有多少个字符未被读取
        char *_base;  // 缓冲区基地址
        int   _flag;  // 读写状态标志位
        int   _file;  // 文件描述符
        int   _charbuf;
        int   _bufsiz; // 缓冲区大小
        char *_tmpfname;
        };
typedef struct _iobuf FILE;

三种刷新缓冲区的操作

  • fclose
  • fflush(fp)
  • 写满

简单读写示例

#include <iostream>
#include <error.h>

int main()
{
    FILE *fp;
    char c,ret;

    fp=fopen("1.txt","r+");
    if(NULL == fp) 
    {   
        perror("fopen");
        goto error;
    }   
    /*  
    // 操作成功返回 该字符,操作失败(已经读到了文件文件末尾) 返回 “EOF”
    while((c = fgetc(fp)) != EOF){
        putchar(c);
    }
    */
    c='H';
    // 运行成功返回该字符,运行失败,返回 EOF(-1)                                                                          
    ret=fputc(c,fp);
    if(EOF != ret)
    {   
        perror("fputc");
        goto error;
    }   
    fclose(fp);
error:
    return 0;
}

fgets 与 fputs

只能读字符串,不能读取整型数据,或者结构体类型的

  • 函数原型 int fputs(const char *s, FILE *stream);

    • 成功返回0,出错返回非零
    • 写入成功返回非负数,失败返回 EOF
  • 函数原型 char *fgets(char *s, int n, FILE *stream)

    • 读取成功时返回字符数组首地址,也即 str;读取失败时返回 NULL
    • 如果开始读取时文件内部指针已经指向了文件末尾,那么将读取不到任何字符,也返回 NULL
    • 读取到的字符串会在末尾自动添加 '\0'
fgets() 遇到换行时,会将换行符一并读取到当前字符串.
gets() 不一样,它会忽略换行符.

fread 与 fwrite

  • 函数原型 size_t fread ( void *ptr, size_t size, size_t count, FILE *fp );
    • 从fp所指定的文件中读取长度为size的n个数据项,存到fp所指向的内存区,成功返回所读的数据项个数,如遇文件结束或者出错返回0
  • 函数原型 size_t fwrite ( void * ptr, size_t size, size_t count, FILE *fp );
    • 把ptr所指向的n*size个字节输出到fp所指向的文件,成功返回写到fp文件里的数据项个数
    • ptr 为内存区块的指针,它可以是数组、变量、结构体等。
      • fread() 中的 ptr 用来存放读取到的数据
      • fwrite() 中的 ptr 用来存放要写入的数据。
    • size:表示每个数据块的字节数。
    • count:表示要读写的数据块的块数。
    • fp:表示文件指针。
      • 理论上,每次读写 size*count 个字节的数据。
      • 对于 fwrite() 来说,肯定发生了写入错误,可以用 ferror() 函数检测。
      • 对于 fread() 来说,可能读到了文件末尾,可能发生了错误,可以用 ferror() 或 feof() 检测。
示例
 #define  N 2
  struct stu{
      char name[10];
      int num;
      int age;
      float score;
  }boya_t[N],boyb_t[N],*pa,*pb;
  void work4()
  {
      FILE *fp;
      pa = boya_t;
      pb = boyb_t;
      int i;
      if((fp=fopen("1.txt","wb+")) == NULL)
      {   
          perror("fopen");
          goto err;
      }   
      printf("Input data:\n");
      for (i=0;i<N;++i,pa++)
      {   
          scanf("%s %d %d %f",pa->name,&pa->num,&pa->age,&pa->score);
      }   
      fwrite(boya_t,sizeof(struct stu),N,fp);
      rewind(fp);
      fread(boyb_t,sizeof(struct stu),N,fp);
      for (i=0;i<N;++i,pb++)
      {   
       printf("%s %d %d %5.2f\n",pb->name,pb->num,pb->age,pb->score);   
      }   
      fclose(fp);
  
  err:
      return;
  }
  void work5()
  {
      FILE *fp;
      int i;
      pb = boyb_t;
      if((fp=fopen("1.txt","rb+")) == NULL)
      {   
          perror("fopen");
          goto err;
      }   
      fread(boyb_t,sizeof(struct stu),N,fp);
      for (i=0;i<N;++i,pb++)
      {   
       printf("%s %d %d %5.2f\n",pb->name,pb->num,pb->age,pb->score);   
      }   
      fclose(fp);
  err:
      return;
  }
  
  int main()
  {
      work5();                                                                                                             
      return 0;
  }
  

fscanf 与 fprintf

  • fscanf() 和 fprintf() 和前面使用的 scanf() 和 printf() ,功能相似都是格式化读写函数
    • 区别fscanf 和 fprintf 的读写对象不是键盘和显示器, 而是磁盘文件
  • 函数原型 int fscanf ( FILE *fp, char * format, ... );
  • 函数原型 int fprintf ( FILE *fp, char * format, ... );
    • fprintf() 返回成功写入的字符的个数,失败则返回负数.fscanf() 返回参数列表中被成功赋值的参数个数。
示例
  void work4()
  {
      FILE *fp;
      int i;
      pa = boya_t;
      pb = boyb_t;
      if((fp = fopen("1.txt","wt+")) == NULL)
      {   
          perror("fopen");
          goto err;
      }   
      for (i=0;i<N;++i,pa++)
      {   
          scanf("%s %d %d %f",pa->name,&pa->num,&pa->age,&pa->score);
      }   
      pa = boya_t;
      for (i=0;i<N;++i,pa++)
      {   
          // 将boya中的数据写入到文件
       fprintf(fp,"%s %d %d %f\n",pa->name,pa->num,pa->age,pa->score);
      }   
      // 重置文件指针
      rewind(fp);
      // 从文件中读取数据,保存到boyb中
      for (i=0;i<N;++i,pb++)
      {   
          fscanf(fp,"%s %d %d %f\n",pb->name,&pb->num,&pb->age,&pb->score);
      }   
      pb=boyb_t;
      for (i=0;i<N;++i,pb++)
      {   
          printf("%s %d %d %5.2f\n",pb->name,pb->num,pb->age,pb->score);
      }   
      fclose(fp);
  err:
      return;
  }

rewind 和 fseek 随机读写文件

实现随机读写的关键是要按要求移动位置指针,这称为文件的定位。常用函数有两个 rewind() 和 fseek()。

  • 函数原型 void rewind ( FILE *fp );
  • 函数原型 int fseek ( FILE *fp, long offset, int origin );
    • fp 为文件指针,也就是被移动的文件。
    • offset 为偏移量,也就是要移动的字节数。之所以为 long 类型,是希望移动的范围更大,能处理的文件更大。
      • offset 为正时,向后移动;offset 为负时,向前移动。
    • origin 为起始位置,也就是从何处开始计算偏移量。
    • fseek() 一般用于二进制文件
    • ftell()函数配合fseek函数可以获得文件大小
      起始点 常量名 常量值
      文件开头 SEEK_SET 0
      当前位置 SEEK_CUR 1
      文件末尾 SEEK_END 2
示例
  void work6()
  {
      FILE *fp;
      int i;
      pa = boya_t;
      if((fp = fopen("1.txt","wb+")) == NULL)
      {
          perror("fopen");
          goto err;
      }
      printf("Input data:\n");
      for (i=0;i<N;++i,pa++)
      {
       scanf("%s %d %d %f",pa->name,&pa->num,&pa->age,&pa->score);
      }
      fwrite(boya_t,sizeof(struct stu),N,fp);
      fseek(fp,sizeof(struct stu),SEEK_SET);
      fread(&boyb_t,sizeof(struct stu),1,fp);
      printf("%s %d %d %5.2f\n",boyb_t.name,boyb_t.num,boyb_t.age,boyb_t.score);
      fclose(fp);
  err:
      return;
  }

拷贝文件

#include <iostream>
#include <string.h>

void demo1(int argc,char *old_file,char *new_file)
{
    if(argc != 3)
    {   
        perror("err: ./demo oldFile newFile");
        return;
    }   
    FILE *fp_in,*fp_out;
    char *buff;
    int ret;
    if((fp_in = fopen(old_file,"rb")) == NULL)
    {   
        perror("fopen old_file");
        goto err;
    }   
    if((fp_out = fopen(new_file,"wb+")) == NULL)
    {   
        perror("fopen new_file");
        goto err;
    }   
    buff=(char*)malloc(1024);
    while(1)
    {   
        ret = fread(buff,1,1024,fp_in);
       if(ret != 1024) 
       {   
           fwrite(buff,ret,1,fp_out);
       }else{
           fwrite(buff,1024,1,fp_out);
       }   
       if(feof(fp_in))
           break;                                                                                                          
    }   


    fclose(fp_in);
    fclose(fp_out);
err:
return;
}

int main(int argc,char *argv[])
{
    demo1(argc,argv[1],argv[2]);
    return 0;
}

feof

  • 函数原型 int feof(FILE * stream);
    • 返回值 检测到文件结束标识返回1,否则返回0。
  • feof()与EOF不同:
    • feof()是函数,用来检测文件的结束
    • 标准库中定义的宏,定义为:#define EOF (-1)。

read 与 write

概述
#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);

描述
read() 从文件描述符 fd 中读取 count 字节的数据并放入从 buf 开始的缓冲区中.
如果 count 为零,read()返回0,不执行其他任何操作. 如果 count 大于SSIZE_MAX,那么结果将不可预料.


返回值
成功时返回读取到的字节数(为零表示读到文件描述符), 此返回值受文件剩余字节数限制.当返回值小于指定的字节数时并不意味着错误;这可能是因为当前可读取的字节数小于指定的字节数(比如已经接近文件结尾,或者正在从管道或者终端读取数据,或者 read()被信号中断). 发生错误时返回-1,并置 errno 为相应值.在这种情况下无法得知文件偏移位置是否有变化.
错误代码
EINTR
在读取到数据以前调用被信号所中断.
EAGAIN
使用 O_NONBLOCK 标志指定了非阻塞式输入输出,但当前没有数据可读.
EIO
输入输出错误.可能是正处于后台进程组进程试图读取其控制终端,但读操作无效,或者被信号SIGTTIN所阻塞, 或者其进程组是孤儿进程组.也可能执行的是读磁盘或者磁带机这样的底层输入输出错误.
EISDIR
fd 指向一个目录.
EBADF
fd 不是一个合法的文件描述符,或者不是为读操作而打开.
EINVAL
fd 所连接的对象不可读.
EFAULT
buf 超出用户可访问的地址空间.
也可能发生其他错误,具体情况和 fd 所连接的对象有关. POSIX 允许 read 在读取了一定量的数据后被信号所中断,并返回 -1(且 errno 被设置为EINTR),或者返回已读取的数据量.

兼容于
SVr4, SVID, AT&T, POSIX, X/OPEN, BSD 4.3
限制
在NFS文件系统中,读取小块数据仅更新时间标记,之后的调用不再读取服务器端的数据.这是因为客户端把数据放在缓存里. 由于大多数情况下不存在NFS服务器向客户端的读操作, 所以NFS客户必须将更新时间标记的操作放在服务器端,而数据可以放在客户端的缓存里留待以后更新.UNIX也可以禁用客户端的缓存,但那样的话大多数情况下会导致服务器性能下降.

·write函数

1.功能

将数据写入已打开的文件内

2.相关函数
open,read,fcntl,close,lseek,sync,fsync,fwrite
3.表头文件
#include<unistd.h>
4.定义函数
ssize_t write (int fd,const void * buf,size_t count);
5.函数说明
write()会把参数buf所指的内存写入count个字节到参数fd所指的文件内。当然,文件读写位置也会随之移动。
6.返回值
如果顺利write()会返回实际写入的字节数。当有错误发生时则返回-1,错误代码存入errno中。
7.错误代码
EINTR 此调用被信号所中断。
EAGAIN 当使用不可阻断I/O 时(O_NONBLOCK),若无数据可读取则返回此值。
EBADF 参数fd非有效的文件描述词,或该文件已关闭。



read函数
1.功能

由已打开的文件读取数据
2.相关函数 

readdir,write,fcntl,close,lseek,readlink,fread
3.表头文件 

#include<unistd.h>
4.定义函数 

ssize_t read(int fd,void * buf ,size_t count); 
5.函数说明 
read()会把参数fd所指的文件传送count个字节到buf指针所指的内存中。若参数count为0,则read()不会有作用并返回0。返回值为实际读取到的字节数,如果返回0,表示已到达文件尾或是无可读取的数据,此外文件读写位置会随读取到的字节移动。  
6.附加说明 

如果顺利read()会返回实际读到的字节数,最好能将返回值与参数count作比较,若返回的字节数比要求读取的字节数少,则有可能读到了文件尾、从管道(pipe)或终端机读取,或者是read()被信号中断了读取动作。当有错误发生时则返回-1,错误代码存入errno中,而文件读写位置则无法预期。  
错误代码 EINTR 此调用被信号所中断。 
EAGAIN 当使用不可阻断I/O时(O_NONBLOCK),若无数据可读取则返回此值。 
EBADF 参数fd 非有效的文件描述词,或该文件已关闭。

错误集合

错误信息:

EACCES, EPERM:用户试图在套接字广播标志没有设置的情况下连接广播地址或由于防火墙策略导致连接失败。

EADDRINUSE:本地地址处于使用状态。

EAFNOSUPPORT:参数serv_add中的地址非合法地址。

EAGAIN:没有足够空闲的本地端口。

EALREADY:套接字为非阻塞套接字,并且原来的连接请求还未完成。

EBADF:非法的文件描述符。

ECONNREFUSED:远程地址并没有处于监听状态。

EFAULT:指向套接字结构体的地址非法。

EINPROGRESS:套接字为非阻塞套接字,且连接请求没有立即完成。

EINTR:系统调用的执行由于捕获中断而中止。

EISCONN:已经连接到该套接字。

ENETUNREACH:网络不可到达。

ENOTSOCK:文件描述符不与套接字相关。

ETIMEDOUT:连接超时。

[部分原文链接]https://blog.csdn.net/lyc_daniel/article/details/12998329

posted on 2021-08-31 09:19  lodger47  阅读(112)  评论(0)    收藏  举报

导航