[Linux C編程]文件操作

文件操作

1.什麼是系統調用?

  所謂系統調用是指操作系統提供給用戶的一組“特殊”接口,用戶程序可以通過這組“特殊”接口來獲得操作系統內核提供的服務。

2.爲什麼用戶程序不能直接訪問系統內核提供的服務呢?

  由於在Linux中,爲了更好地保護內核空間,將程序的運行空間分爲內核空間和用戶空間(也就是常稱的內核態和用戶態),它們分別運行在不同的級別上,在邏輯上是相互隔離的。

  因此,用戶進程在通常情況下不允許訪問內核數據,也無法使用內核函數,它們只能在用戶空間操作用戶數據,調用用戶空間的函數。

3.什麼是文件?linux如何看待文件?

Linux一點哲學,“一切皆爲文件”;在Linux中對目錄和設備的操作都等同於對文件的操作,都是使用文件描述符來進行的。

Linux文件可分爲:普通文件,目錄文件,鏈接文件,設備文件

4.什麼是文件描述符?Linux內核如何分配描述符?

  文件描述符是一個非負的整數,它是一個索引值,並指向內核中每個進程打開文件的記錄表。

  當打開一個現存文件或創建一個新文件時,內核就向進程返回一個文件描述符;當需要讀寫文件時,也需要把文件描述符作爲參數傳遞給相應的函數。

  一個進程啓動時,都會打開3個文件:標準輸入、標準輸出和標準出錯處理。

 

5.系統調用API

(1) creat

函數的作用: 創建一個文件;

函數的原型: int  creat(const char *pathname, mode_t mode);

filename:創建的文件名(包含路徑,缺省爲當前路徑)

mode:創建模式:S_IRUSR   可讀

          S_IWUSR   可寫

          S_IXUSR    可執行

          S_IXRWU   可讀可寫可執行

(除用以上宏來選擇創建模式,也可以用數字來表示,如0755)

文件頭:  #include <sys/types.h>

       #include <sys/stat.h>

       #include <fcntl.h>

返回值:成功:新的文件描述符;

     出錯:  -1

(2) open

函數的作用:打開或創建文件,在打開或創建文件時可以指定文件的屬性及用戶的權限等各種參數;

函數的原型:

int  open(const char *pahtname, int flags);

int  open(const char *pahtname, int flags, mode_t mode);

返回值:文件描述符---成功;出錯:-1;

flags:

參數: O_RDONLY:只讀打開

     O_WRONLY:只寫打開

     O_RDWR:讀、寫打開

     O_CREAT:如果此文件不存在則創建它,使用此選項時,需同時說明第三個參數

mode,用其說明該新文件的存取權限;

O_NONBLOCK:如果path name指的是一個塊特殊文件或一個字符特殊文件,則此選

擇項爲此文件的本次打開操作和後續的I/O操作設置非阻塞方式。

      O_APPEND:原來有內容,則會自動保留文件內容,自動向下讀寫;

      O_TRUNC:  文件存在,有內容,文件清空;

(3)read

函數的作用:從打開的文件中讀取count個字節數據到buf所指向的緩衝區中。

函數的原型:ssize_t  read(int fd, void *buf,  size_t count);

包含的頭文件: #include  <unistd.h>

返回值:正常是實際讀到的字節數;

     如果是在文件結束或者是無數據,返回0;

     出錯,-1;

(4)write

函數的作用: 把count個字節從buf所指向的緩衝區中寫到文件描述符fd所指向的文件中

函數的原型: ssize_t   write(int fd, const void *buf, size_t count);

頭文件:  #include <unistd.h>    

返回值:  成功會返回實際寫入的字節數;

       出錯:-1;

(5)lseek

函數的功能:將文件讀寫指針相對whence移動offset個字節。

函數的原型:int lseek(int fd, offset_t  offset, int whence);

函數的參數:offset: 指針的微調,在指定的指針向前移動爲負, 向後爲正;

        whence:  SEEK_SET:放在文件頭

               SEEK_CUR:當前的位置;

               SEEK_END:  文件尾;

返回值:返回文件當前指針到文件開始的地方有多少字節;

    出錯-1;

6.標準庫函數

C庫函數的文件操作是獨立於具體的操作系統平臺的。

7.什麼是不帶緩存I/O操作?什麼是帶緩存I/O操作?

不帶緩存的I/O是對文件描述符操作,帶緩存的I/O是針對流的。

標準I/O庫就是帶緩存的I/O,它由ANSI C標準說明。當然,標準I/O最終都會調用上面的I/O例程。

標準I/O庫代替用戶處理很多細節,比如緩存分配、以優化長度執行I/O等。

標準I/O提供緩存的目的就是減少調用read和write的次數,它對每個I/O流自動進行緩存管理(標準I/O函數通常調用malloc來分配緩存)。

它提供了三種類型的緩存:
1) 全緩存。當填滿標準I/O緩存後才執行I/O操作。磁盤上的文件通常是全緩存的。
2) 行緩存。當輸入輸出遇到新行符或緩存滿時,才由標準I/O庫執行實際I/O操作。stdin、stdout通常是行緩存的。

3) 無緩存。相當於read、write了。stderr通常是無緩存的,因爲它必須儘快輸出。

一般而言,由系統選擇緩存的長度,並自動分配。標準I/O庫在關閉流的時候自動釋放緩存。

在標準I/O庫中,一個效率不高的不足之處是需要複製的數據量。

當使用每次一行函數fgets和fputs時,通常需要複製兩次數據:

第一次是在內核和標準I/O緩存之間(當調用read和write時),

第二次是在標準I/O緩存(通常系統分配和管理)和用戶程序中的行緩存(fgets的參數就需要一個用戶行緩存指針)之間。

8.庫函數

(1)fopen

函數的作用: 打開文件

函數的原型: FILE *fopen(const char *pth, const char *mode)

mode:  r:讀,文件必須存在;

     r+:打開可讀寫,文件必須存在;

     w:打開只寫文件,文件不存在就會創建文件; 文件清0;

     w+:打開可讀寫的文件,

     a:附加的形式打開只寫文件,不存在就創建,存在就寫到原來的文件尾。

     a+:以附加的形式打開可讀寫的文件,不存在就創建,存在就寫到原來的文件尾。

     b:二進制文件

 頭文件: #include <stdio.h>

 返回值: 成功是指向=文件流的指針;

       出錯返回NULL;     

(2)fclose

函數的作用:關閉先前fopen()打開的文件,會將緩衝區內的數據寫入文件中,並釋放系統所提供的文件資源返回值。

函數的原型:int fclose(FILE * stream);

頭文件:#include<stdio.h>

返回值:若關動作成功則返回0

     出錯返回EOF並把錯誤代碼存到errno.

 

注:對文件的讀和寫是最常用的文件操作。在Linux C中提供了多種文件讀寫的函數

字符讀寫函數:fgetc和fputc

字符串讀寫函數:fgets和fputs

數據塊讀寫函數:fread和fwrite

格式化讀寫函數:fscanf和fprintf

(3)fputc

函數的作用:  將一個指定的字符寫入到文件流中;

函數的原型:  int fputc(int c, FILE *stream);

返回值:   返回寫入成功的字符,c;  EOF則表示失敗。

(4)fgetc

函數的作用:從文件流中讀取一個字符

函數原型: int fgetc(FILE *stream)

返回值:返回值正常的是讀取的字符;EOF表示到了文件尾;

(5)fputs

函數的作用:將一個字符串寫入到文件內

函數的原型: int fputs(const char *s, FILE *stream)

返回值:成功返回寫成字符數; EOF表示出錯

(6)fgets

函數的作用:從文件中讀取一個字符串;

函數的原型:char *fgets(char *s, int size,  FILE *steam)

函數的說明:fgets()用來從參數stream所指的文件內讀入字符並存到參數s所指的內存空間中,知道出現換行字符、讀到文件尾或是已讀了size-1個字符爲止,最後會加上NULL作爲字符串結束。

返回值: 成功返回s, 出錯NULL。

(7)fread

函數的作用:從文件流中讀取數據塊

函數原型:size_t  fread(void *ptr,  size_t size, size_t  nmemb, FILE * stream);

函數參數:stream爲已打開的文件指針

      ptr:指向欲存放讀取進來的數據空間

      讀取的字符數以參數nmemb來覺得

返回值:返回實際讀到數據塊的數目

     比nmember小的話,可能是到了文件尾,或者錯誤發生。

     feof() :到文件尾;

     ferror():判斷錯誤的

(8) fwrite

函數的作用:將數據塊寫到文件流中:

函數原型: size_t  fwrite(const void * ptr,  size_t size,  size_t nmemb,  FILE *stream);

函數參數:stream爲已打開的文件指針,

       ptr:指向欲寫入的數據地址,總共寫入的字符數以參數nmemb來決定

返回值: 實際寫入的nmemb數目;

(9)fprintf

函數的作用:格式化數據到文件

函數的原型: int fprintf(FILE *stream, const char *format, ....);

返回值:成功返回實際輸入的字符數,失敗-1;

(10)fscanf

函數的作用: 格式化字符串輸入

函數的原型: int fscanf(FILE *strem, const char *fromat,....)

返回值:成功返回參數數目,出錯-1

(11)fseek

函數的作用:移動文件流的讀寫位置

函數的原型: int fseek(FILE * stream, long offset, int whence)

返回值:成功返回0, 出錯-1;

(12)ftell

函數的作用:讀取文件流的讀寫位置;

函數的原型:long ftell(FILE * stream)

返回值: 成功返回當前的讀寫位置;

      出錯-1;

(13)feof

函數的作用: 檢測文件流是否到了文件尾

函數的原型:int feof(FILE *steam)

返回值:  非零代表到了文件尾,其他是0;

 

9.帶緩存的I/O操作如何完成文件的複製?

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h> 

#define BUFFER_SIZE 1024 

int main(int argc,char *argv[]) 
{ 
		int from_fd,to_fd; 
		int bytes_read,	bytes_write; 
		char buffer[BUFFER_SIZE]; 
		char *ptr; 

		if(argc!=3) 
		{ 
			fprintf(stderr,"Usage:%s fromfile tofile/n/a",argv[0]); 
			exit(1); 
		} 
		if((from_fd=open(argv[1],O_RDONLY))==-1) 
		{ 
			fprintf(stderr,"Open %s Error:%s/n",argv[1], strerror(errno)); 
			exit(1); 
		} 
		if((to_fd=open(argv[2],O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR))==-1) 
		{ 
				fprintf(stderr,"Open %s Error:%s/n",argv[2],strerror(errno)); 
				exit(1); 
		} 
		while(bytes_read=read(from_fd,buffer,BUFFER_SIZE)) 
		{ 
	
			if((bytes_read==-1)&&(errno!=EINTR))  	/* 一個致命的錯誤發生了 */ 
				break; 
			else if(bytes_read>0) 
			{ 
				ptr=buffer; 
				while(bytes_write=write(to_fd,ptr,bytes_read)) 
				{ 
					if((bytes_write==-1)&&(errno!=EINTR))      /* 一個致命錯誤發生了 */ 
						break; 
					else if(bytes_write==bytes_read)           /* 寫完了所有讀的字節 */ 
					        break; 
					/* 只寫了一部分,繼續寫 */ 
		 			else if(bytes_write>0) 
					{ 
						ptr+=bytes_write; 
						bytes_read-=bytes_write; 
					} 
				} 
				/* 寫的時候發生的致命錯誤 */ 
				if(bytes_write==-1)
					break; 

			} 
		} 

		close(from_fd); 
		close(to_fd); 
		exit(0); 
} 



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章