zoukankan      html  css  js  c++  java
  • Linux(C/C++)下的文件操作open、fopen与freopen via Boblim

    Linux(C/C++)下的文件操作open、fopen与freopen

    open是linux下的底层系统调用函数,fopen与freopen c/c++下的标准I/O库函数,带输入/输出缓冲。
    linxu下的fopen是open的封装函数,fopen最终还是要调用底层的系统调用open。
    所以在linux下如果需要对设备进行明确的控制,那最好使用底层系统调用(open),

    open对应的文件操作有:close, read, write,ioctl 等。
    fopen 对应的文件操作有:fclose, fread, fwrite, freopen, fseek, ftell, rewind等。
    freopen用于重定向输入输出流的函数,该函数可以在不改变代码原貌的情况下改变输入输出环境,但使用时应当保证流是可靠的。详细见第3部分。


    open和fopen的区别:
    1,fread是带缓冲的,read不带缓冲.
    2,fopen是标准c里定义的,open是POSIX中定义的.
    3,fread可以读一个结构.read在linux/unix中读二进制与普通文件没有区别.
    4,fopen不能指定要创建文件的权限.open可以指定权限.
    5,fopen返回文件指针,open返回文件描述符(整数).
    6,linux/unix中任何设备都是文件,都可以用open,read.


    1、open系统调用(linux)

    需要包含头文件:#include<sys/types.h>
                                #include<sys/stat.h>
                                #include<fcntl.h> 
    
    函数原型:int open( const char * pathname, int oflags);
                      int open( const char * pathname,int oflags, mode_t mode);
              
    mode仅当创建新文件时才使用,用于指定文件的访问权限。
    
    pathname 是待打开/创建文件的路径名;
    
    oflags用于指定文件的打开/创建模式,这个参数可由以下常量(定义于 fcntl.h)通过逻辑或构成。
       O_RDONLY       只读模式 
       O_WRONLY      只写模式 
       O_RDWR          读写模式
    以上三者是互斥的,即不可以同时使用。
    
    打开/创建文件时,至少得使用上述三个常量中的一个。以下常量是选用的:
       O_APPEND         每次写操作都写入文件的末尾 
       O_CREAT            如果指定文件不存在,则创建这个文件 
       O_EXCL               如果要创建的文件已存在,则返回 -1,并且修改 errno 的值
       O_TRUNC           如果文件存在,并且以只写/读写方式打开,则清空文件全部内容 
       O_NOCTTY         如果路径名指向终端设备,不要把这个设备用作控制终端。
       O_NONBLOCK   如果路径名指向 FIFO/块文件/字符文件,则把文件的打开和后继 I/O设置为非阻塞模式(nonblocking mode)。
    //以下用于同步输入输出
       O_DSYNC          等待物理 I/O 结束后再 write。在不影响读取新写入的数据的前提下,不等待文件属性更新。 
       O_RSYNC          read 等待所有写入同一区域的写操作完成后再进行
       O_SYNC            等待物理 I/O 结束后再 write,包括更新文件属性的 I/O
    
    
    当你使用带有O_CREAT标志的open调用来创建文件时,你必须使用有3个参数格式的open调用。第三个参数mode是几个标志按位或后得到的,
    这些标志在头文件sys/stat.h中定义,如下所示:
      S_IRUSR:    读权限,文件属主
      S_IWUSR:   写权限,文件属主
      S_IXUSR:    执行权限,文件属主
      S_IRGRP:    读权限,文件所属组
      S_IWGRP:   写权限,文件所属组
      S_IXGRP:    执行权限,文件所属组
      S_IROTH:   读权限,其它用户
      S_IWOTH:  写权限,其它用户
      S_IXOTH:   执行权限,其它用户
    
    返回值:成功则返回文件描述符,否则返回 -1。  返回文件描述符(整型变量0~255)。由open 返回的文件描述符一定是该进程尚未使用的最小描述符。只要有一个权限被禁止则返回-1。
    错误代码:(均已E开头,将其去掉就是有关于错误的方面的单词或单词的缩写)
      EEXIST 参数pathname 所指的文件已存在,却使用了O_CREAT和O_EXCL旗标。
      EACCESS 参数pathname所指的文件不符合所要求测试的权限。
      EROFS 欲测试写入权限的文件存在于只读文件系统内。
    
      EFAULT 参数pathname指针超出可存取内存空间。
    
      EINVAL 参数mode 不正确。
      ENAMETOOLONG 参数pathname太长。
    
      ENOTDIR 参数pathname不是目录。
      ENOMEM 核心内存不足。
      ELOOP 参数pathname有过多符号连接问题。
      EIO I/O 存取错误。
    
    
    -------------------------------------------------------------------------------------------------------------------
    ssize_t write(int fd, const void *buf, size_t count);
    参数:   
    fd:要进行写操作的文件描述词。
    buf:需要输出的缓冲区
    count:最大输出字节计数  
    
    返回值:成功返回写入的字节数,出错返回-1并设置errno
    -----------------------------------------------.--------------------------------------------------------------------
    ssize_t read(int fd, void *buf, size_t count);  
    参数:      
    buf:需要读取的缓冲区
    count:最大读取字节计数            
    
    返回值:成功返回读取的字节数,出错返回-1并设置errno,如果在调read之前已到达文件末尾,则这次read返回0 。  
    
    -------------------------------------------------------------------------------------------------------------------
    
    

    2、fopen库函数

    头文件:<stdio.h>
    函数原型:FILE * fopen(const char * path, const char * mode);
    path字符串包含欲打开的文件路径及文件名,参数mode字符串则代表着流形态。
    mode有下列几种形态字符串:
    "r"或"rb"        以只读方式打开文件,该文件必须存在。
    "w"或"wb"     以写方式打开文件,并把文件长度截短为零。
    "a"或"ab"      以写方式打开文件,新内容追加在文件尾。
    "r+"或"rb+"或"r+b"       以更新方式打开(读和写)
    "w+"或"wb+"或"w+b"   以更新方式打开,并把文件长度截短为零。
    "a+"或"ab+"或"a+b"     以更新方式打开,新内容追加在文件尾。
    字母b表示文件时一个二进制文件而不是文本文件。(linux下不区分二进制文件和文本文件)
    返回值:文件顺利打开后,指向该流的文件指针就会被返回。如果文件打开失败则返回NULL,并把错误代码存在errno 中。
    
    -------------------------------------------------------------------------------------------------------------------
    fread是一个函数。从一个文件流中读数据,最多读取count个元素,每个元素size字节,如果调用成功返回实际读取到的元素个数,如果不成功或读到文件末尾返回 0。
    函数原型:size_t fread ( void *buffer, size_t size, size_t count, FILE *stream) ;
    参 数:
    buffer:用于接收数据的内存地址
    size:要读写的字节数,单位是字节
    count:要进行读写多少个size字节的数据项,每个元素是size字节.
    stream:输入流
    返回值:实际读取的元素个数.如果返回值与count不相同,则可能文件结尾或发生错误,从ferror和feof获取错误信息或检测是否到达文件结尾.
    -------------------------------------------------------------------------------------------------------------------
    fwrite:向文件写入一个数据块
    函数原型:size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream);
    参数:
    buffer:是一个指针,对fwrite来说,是要获取数据的地址;
    size:要写入内容的单字节数;
    count:要进行写入size字节的数据项的个数;
    stream:目标文件指针;
    返回值:返回实际写入的数据块数目
    -------------------------------------------------------------------------------------------------------------------
    fflush:把文件流里的所有为写出数据立刻写出。
    函数原型:int fflush(FILE *stream);
    -------------------------------------------------------------------------------------------------------------------
    fseek:是lseek系统调用对应的文件流函数。它在文件流里为下一次读写操作指定位置。
    函数原型:int fseek(FILE *stream, long offset, int fromwhere);
    参数stream为文件指针
    参数offset为偏移量,正数表示正向偏移,负数表示负向偏移
    参数fromwhere设定从文件的哪里开始偏移,可能取值为:SEEK_CUR、 SEEK_END 或 SEEK_SET
    SEEK_SET: 文件开头
    SEEK_CUR: 当前位置
    SEEK_END: 文件结尾
    其中SEEK_SET,SEEK_CUR和SEEK_END依次为0,1和2.
    返回值:如果执行成功,stream将指向以fromwhere为基准,偏移offset(指针偏移量)个字节的位置,函数返回0。如果执行失败(比如offset超过文件自身大小),则不改变stream指向的位置,函数返回一个非0值。
    

    以下为linux下一个打开文件并显示文件内容的程序:

    
    #include <stdio.h>  
    #include <stdlib.h>  
    #include <unistd.h>  
    #include <fcntl.h>  
    #include <string.h>  
    #include <sys/types.h>  
    #include <sys/stat.h>  
      
      
    charchar * FILE_NAME = "/home/hzg/uart/download.bin";     
    unsigned char file_buffer[20];  
      
      
    int main()  
    {  
        FILEFILE * file_fd;  
        int read_len, i;  
      
        file_fd = fopen(FILE_NAME,"rb");  
        if(file_fd == NULL)  
        {  
           perror("errno");  
        }  
        else   
        {  
           printf("File Open successed!
    ");  
        }  
      
        while(1)  
        {  
           read_len = fread(file_buffer, 1, 16, file_fd);  
             
           if(read_len == -1)  
           {  
               printf("File read error!
    ");  
               perror("errno");  
               exit(0);  
           }  
           else if(read_len == 0)  
           {  
               printf("File read Over!
    ");  
               break;  
           }  
           else   
           {  
               printf("Read %d Byte From download.bin: ",read_len);  
               for(i = 0; i < read_len; i++)  
               {  
                    printf(" %02x",file_buffer[i]);  
               }  
               printf("
    ");  
           }  
          
           usleep(20000);  
        }  
      
        fclose(file_fd);  
        return 0;  
    } 
    

    3、freopen

    函数原型:FILE * freopen ( const char * filename, const char * mode, FILE * stream );
    参数:
    filename: 要打开的文件名
    mode: 文件打开的模式,和fopen中的模式(r/w)相同
    stream: 文件指针,通常使用标准流文件(stdin/stdout/stderr)
    返回值:如果成功则返回该指向该stream的指针,否则为NULL。
    作用:用于重定向输入输出流的函数,将stream中的标准输入、输出、错误或者文件流重定向为filename文件中的内容。linux下需要重定向输出很容易使用 ./程序名 >test (>>test 追加),windows下的输入输出重定向可以使用freopen。
    
    使用方法: 因为文件指针使用的是标准流文件,因此我们可以不定义文件指针。
    
    我们使用freopen()函数以只读方式r(read)打开输入文件test.in ,freopen("test.in", "r", stdin);
    
    这样程序的输入就会从标准输入流stdin转换到从文件"test.in"中输入
    
    然后使用freopen()函数以写入方式w(write)打开输出文件test.out,freopen("test.out", "w", stdout);
    程序的输出就会从原来的标准输出变成写入文件"test.out"中
    
    

    
    
    #define METHOD1
    
    #ifdef METHOD1
    //方法1:
     #include <stdlib.h>
     #include <stdio.h>
     #include <unistd.h>
     #include <sys/stat.h>
     #include <fcntl.h>
     
     
     int main()
     {
         fflush(stdout);
         setvbuf(stdout,NULL,_IONBF,0);
         printf("test stdout
    ");
         int save_fd = dup(STDOUT_FILENO); // 保存标准输出 文件描述符 注:这里一定要用 dup 复制一个文件描述符. 不要用 = 就像是Winodws下的句柄.
         int fd = open("test1.txt",(O_RDWR | O_CREAT), 0644);
         dup2(fd,STDOUT_FILENO); // 用我们新打开的文件描述符替换掉 标准输出
         printf("test file
    ");
    	 
    	 //再恢复回来标准输出. 两种方式
    	 //方法1 有保存 标准输出的情况
    	 //dup2(save_fd,STDOUT_FILENO);
    	 
    	 //方法2 没有保存 标准输出的情况
          int ttyfd = open("/dev/tty",(O_RDWR), 0644);
         dup2(ttyfd,STDOUT_FILENO);
         printf("test tty
    ");
    
         fclose(stdout); 
     }
     
    #else
    
    // 方法2:
    #include <stdlib.h> 
    #include <stdio.h> 
    #include <unistd.h> 
     
     int main()
     {
         fflush(stdout);
         setvbuf(stdout,NULL,_IONBF,0);
         //输出到tty控制台
         printf("test stdout
    ");
    
         //输出到test1.txt ,每次根据时间 生成文件名20180822.log
         //判度该文件是否存在,存在就写文件,不存在就创建,并写文件。  简单的log ok
         //freopen实现数据重定向到文件中,
         //返回值: 成功,则返回文件指针;失败,返回NULL(可以不使用它的返回值)
    
    //注: 不要使用这类的代码 stdout = fopen("test1.txt","w");   
    //这样的话输出很诡异的. 最好使用  freopen 这类的函数来替换它.
    
         if(freopen("test1.txt","w",stdout)==NULL)
         {
    	fprintf(stderr, "error redirecting stdout
    ");//error
    	//return
         }
         printf("test file
    ");
         
         //输出到tty控制台
         freopen("/dev/tty","w",stdout);
         printf("test tty
    ");
    
         fclose(stdout); 
     }
    #endif
    
    
  • 相关阅读:
    我们的CPU遭到攻击[LOJ558]
    历史[ZJOI2018]
    字符串[LOJ6517]
    奥运公交[LOJ3255]
    BLO-Blockade[POI2008]
    压力[BJOI2013]
    Earthquake[USACO01OPEN]
    暴力写挂[CTSC2018]
    极简教程:数据结构与算法(二)
    DllRegisterServer的调用失败的问题解决方法
  • 原文地址:https://www.cnblogs.com/scotth/p/9588022.html
Copyright © 2011-2022 走看看