UNIX環境下的C 對二進制流文件的讀寫有兩套班子:1) fopen,fread,fwrite ; 2) open, read, write
在介紹之前先簡單的說一下緩衝區和非緩衝區:
1.緩衝文件系統
緩衝文件系統的特點是:在內存開闢一個“緩衝區”,爲程序中的每一個文件使用,當執行讀文件的操作時,從磁盤文件將數據先讀入內存“緩衝區”, 裝滿後再從內存“緩衝區”依此讀入接收的變量。執行寫文件的操作時
,先將數據寫入內存“緩衝區”,待內存“緩衝區”裝滿後再寫入文件。由此可以看出,內存 “緩衝區”的大小,影響着實際操作外存的次數,內存“緩衝區”越大,則操作外存的次數就少,執行速度就快、效率高。一般來
說,文件“緩衝區”的大小隨機器 而定。fopen, fclose, fread, fwrite, fgetc, fgets, fputc, fputs, freopen, fseek, ftell, rewind等
2.非緩衝文件系統
緩衝文件系統是藉助文件結構體指針來對文件進行管理,通過文件指針來對文件進行訪問,既可以讀寫字符、字符串、格式化數據,也可以讀寫二進制數 據。非緩衝文件系統依賴於操作系統,通過操作系統的功能對文件進行
讀寫,是系統級的輸入輸出,它不設文件結構體指針,只能讀寫二進制文件,但效率高、速度 快,由於ANSI標準不再包括非緩衝文件系統,因此建議大家最好不要選擇它。本書只作簡單介紹。open, close, read, write, getc, getchar, putc, putchar 等。
open&&fopen
open 是POSIX 定義的,是系統調用 返回的是文件描述符(int型整數),open系列只能用在 POSIX 的操作系統上。fopen是ANSIC標準中的C語言庫函數,在不同的系統中應該調用不同的內核api,返回的是一個指向文件結構的指針,fopen系列更具有可移植性
使用open系列函數需要"#include <fcntl.h>" ,鏈接時要之用libc(-lc)
使用fopen系列函數需要"#include <sdtio.h>"
open可以指定權限.
fopen不能指定要創建文件的權限.
open與 read, write 等配合使用,
fopen與 fread, fwrite等配合使用。
前者無緩衝,後者有緩衝
open每次都需要進行內核態和用戶態的切換;
fopen在用戶態下就有了緩存,在進行read和write的時候減少了用戶態和內核態的切換,
表現爲,如果順序訪問文件,fopen系列的函數要比直接調用open系列快;如果隨機訪問文件open要比fopen快。
open屬於低級IO,fopen是高級IO。由於能更多地與操作系統打交道,open系列可以訪問更改一些fopen系列無法訪問的信息,如查看文件的讀寫權限,這些額外的功能通常因系統而異。
文件描述符是linux下的一個概念,linux下的一切設備都是以文件的形式操作.如網絡套接字、管道、硬件設備等。當然包括操作文件。
設備文件不可以當成流式文件來用,只能用open
fopen是用來操縱正規文件的,並且設有緩衝的,跟open還是有一些區別
一般用fopen打開普通文件,用open打開設備文件
用法舉例:
函數名: fopen
功 能: 打開一個流
用 法: FILE *fopen(char *filename, char *type);
程序例:
#include <stdlib.h>
#include <stdio.h>
#include <dir.h>
int main(void)
{
char *s;
char drive[MAXDRIVE];
char dir[MAXDIR];
char file[MAXFILE];
char ext[MAXEXT];
int flags;
s=getenv("COMSPEC"); /* get the comspec environment parameter */
flags=fnsplit(s,drive,dir,file,ext);
printf("Command processor info:\n");
if(flags & DRIVE)
printf("\tdrive: %s\n",drive);
if(flags & DIRECTORY)
printf("\tdirectory: %s\n",dir);
if(flags & FILENAME)
printf("\tfile: %s\n",file);
if(flags & EXTENSION)
printf("\textension: %s\n",ext);
return 0;
}
函數名: open
功 能: 打開一個文件用於讀或寫
用 法: int open(char *pathname, int access[, int permiss]);
程序例:
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
int main(void)
{
int handle;
char msg[] = "Hello world";
if ((handle = open("TEST.$$$", O_CREAT | O_TEXT)) == -1)
{
perror("Error:");
return 1;
}
write(handle, msg, strlen(msg));
close(handle);
return 0;
}
read/write和fread/fwrite區別
read在linux/unix中讀二進制與普通文件沒有區別.
fread可以讀一個結構.
read是帶了緩存但是指的是系統層或者說kernel層,當然也可能不帶,比如直接DMA,由驅動決定。
fread帶緩存指的是應用層帶緩存,
read是內核的緩衝。
fread是標準庫的緩衝,
read/write如果可以精確控制一次讀寫的數據,則會比fread/fwrite更加高效
read/write對應Linux中的system call, 而fread/fwrite則可以說是對read/write的又一次封裝,read/write更加原生,如果不考慮跨平臺,建議多使用read/write.
read函數從打開的設備或文件中讀取數據。
#include <unistd.h> ssize_t read(int fd, void *buf, size_t count); 返回值:成功返回讀取的字節數,出錯返回-1並設置errno,如果在調read之前已到達文件末尾,則這次read返回0
參數count是請求讀取的字節數,讀上來的數據保存在緩衝區buf中,同時文件的當前讀寫位置向後移。注意這個讀寫位置和使用C標準I/O庫時的讀寫位置有可能不同,這個讀寫位置是記在內核中的,而使用C標準I/O庫時的讀
寫位置是用戶空間I/O緩衝區中的位置。
fread就是通過read來實現的,fread是C語言的庫,而read是系統調用
但是差別在read每次讀的數據是調用者要求的大小,比如調用要求讀取10個字節數據,read就會讀10個字節數據到數組中,而fread不一樣,爲了加快讀的速度,fread每次都會讀比要求更多的數據,然後放到緩衝區中,這樣
下次再讀數據只需要到緩衝區中去取就可以了。
如果文件的大小是8k。
你如果用read/write,且只分配了2k的緩存,則要將此文件讀出需要做4次系統調用來實際從磁盤上讀出。
如果你用fread/fwrite,則系統自動分配緩存,則讀出此文件只要一次系統調用從磁盤上讀出。
也就是用read/write要讀4次磁盤,而用fread/fwrite則只要讀1次磁盤。效率比read/write要高4倍。
如果程序對內存有限制,則用read/write比較好。
都用fread 和fwrite,它自動分配緩存,速度會很快,比自己來做要簡單。如果要處理一些特殊的描述符,用read 和write,如套接口,管道之類的
系統調用write的效率取決於你buf的大小和你要寫入的總數量,如果buf太小,你進入內核空間的次數大增,效率就低下。而fwrite會替你做緩存,減少了實際出現的系統調用,所以效率比較高。
如果只調用一次(可能嗎?),這倆差不多,嚴格來說write要快一點點(因爲實際上fwrite最後還是用了write做真正的寫入文件系統工作),但是這其中的差別無所謂。
============
舉個例子:
做如下步驟的操作:
打開文件
讀文件的0k~4k(read or fread)
其他操作
讀文件的1k~3k(read or fread)
關閉文件
這時候如果是read,步驟4要調用內核;而如果是fread,因步驟2在應用層已經緩衝所需內容,數據會直接返回,無需再次調用內核
fread每次會讀取一個緩衝區大小的數據,32位下一般是4096個字節,相當於調用了read(fd,buf,4096)
比如需要讀取512個字節數據,分4次讀取,
調用read就是:
for(i=0; i<4; ++i)
read(fd,buf,128)
一共有4次系統調用
而fread一次就讀取了4096字節放到緩衝區了,所以省事了