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!\n");  
    }  
  
    while(1)  
    {  
       read_len = fread(file_buffer, 1, 16, file_fd);  
         
       if(read_len == -1)  
       {  
           printf("File read error!\n");  
           perror("errno");  
           exit(0);  
       }  
       else if(read_len == 0)  
       {  
           printf("File read Over!\n");  
           break;  
       }  
       else   
       {  
           printf("Read %d Byte From download.bin: ",read_len);  
           for(i = 0; i < read_len; i++)  
           {  
                printf(" %02x",file_buffer[i]);  
           }  
           printf("\n");  
       }  
      
       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\n");
     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\n");
	 
	 //再恢復回來標准輸出. 兩種方式
	 //方法1 有保存 標准輸出的情況
	 //dup2(save_fd,STDOUT_FILENO);
	 
	 //方法2 沒有保存 標准輸出的情況
      int ttyfd = open("/dev/tty",(O_RDWR), 0644);
     dup2(ttyfd,STDOUT_FILENO);
     printf("test tty\n");

     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\n");

     //輸出到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\n");//error
	//return
     }
     printf("test file\n");
     
     //輸出到tty控制台
     freopen("/dev/tty","w",stdout);
     printf("test tty\n");

     fclose(stdout); 
 }
#endif


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM