linux 流函数,标准IO函数库 - 二进制文件IO,流定位,创建临时文件和内存流
1 二進制IO(Binary IO)
在前一篇我們了解了逐字符讀寫和逐行讀寫函數。
如果我們在讀寫二進制文件,希望以此讀寫整個文件內容,這兩個函數雖然可以實現,但是明顯會很麻煩且多次循環明顯效率很低。
為了應對這種場景,標準IO庫提供了fread和fwrite函數。
函數聲明:
#include
size_t fread(void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp);
size_t fwrite(const void *restrict ptr, size_t size size_t nobj, FILE *restrict fp);
函數用法;
a) 讀寫一個數組。
float data[10];
if (write(&data[2], sizeof(float), 4, fp) != 4)
err_sys(“fwrite error");
本例中,從流fp中讀取4個float型數據填入到數組下表從2到5得位置中。
b) 讀寫一個結構體
struct {
short ?count;
long ? total;
char ? name[NAMESIZE];
} item;
if (fwrite(&item, sizeof(item), 1, fp) != 1)
err_sys(“fwrite error");
本例中,從fp讀取數據填入到一個結構體中。
上面兩例都可以認為是讀寫一個結構體的數組,參數size是結構體的長度,參數nobj是數組中要讀寫的元素的個數。
函數返回值:
兩個函數的返回值都是讀寫的元素個數。
對于讀函數,返回值可能會比nobj小,如果有異常拋出或者讀到了文件結尾。這時需要調用函數ferror或feof來判斷。
對于寫函數,返回值比nobj小,則一定是有異常拋出。
函數細節:
在上面的例子中,我們通過fwrite函數填充了一個結構體,那么如果讀寫不在一個系統中,那么結構體的內存布局可能并不相同,這對于現在的多系統互聯工作的場景下很常見。我們會在討論socket時回來繼續看這個問題,實際的解決方案就是在不同系統間讀寫二進制數據時使用相同的協議。
2 定位流(Positioning a Stream)
我們有三種方法對流進行定位:
函數ftell和fseek。將文件的當前偏移位置存儲在long integer型變量中;
函數ftello和fseeko。將文件的當前偏移量存儲在off_t型變量中;
函數fgetpos和fsetpos。使用數據類型fpos_t記錄文件的當前偏移量。
ftell和fseek函數聲明:
#include
long ftell(FILE* fp); ? ?// Returns:current file position indicator if OK, -1L on error
int fseek(FILE* fp, long offset, int whence); ? ? ? // Returns:0 if OK , -1 on error
void rewind(FILE* fp);
函數細節:
二進制文件的偏移量是從文件開始到當前位置的字節數;
ftell函數返回當前文件的偏移位置;
fseek函數用來定位文件到指定偏移位置;
fseek函數的參數whence,用來設置計算偏移量的方法:SEEK_SET表示從文件開頭開始計算,SEEK_CUR表示從文件當前偏移位置開始計算,SEEK_END表示從文件結尾開始計算。
對于一些非Unix操作系統,存儲文本文件的存儲格式會有所不同,當前文件偏移量無法通過字節數來表示,這種情況下,參數whence需要設置為SEEK_SET,并且offset只有兩個值可以使用:0,表示倒回文本開頭;另一個可用值為函數ftell的返回值。
ftello和fseeko函數聲明:
#include
off_t ftello(FILE* fp); ? ? // Returns: current file position indicator if OK, (off_t) -1 on error
int fseeko(FILE* fp, off_t offset, int whence); ? ? /// Returns: 0 if OK, -1 on error
函數細節:
這兩個函數和上面的ftell和fseek功能相同,只是返回值類型不是long,而改成了off_t,實現上可以讓off_t的表示范圍更大。
fgetpos和fsetpos函數聲明:
#include
int fgetpos(FILE* restrict fp, fpos_t *restrict pos);
int fsetpos(FILE* fp, const fpos_t pos);
函數細節:
fgetpos函數保存當前文件偏移量到參數pos中
fgetpos得到的pos可以用來使用fsetpos設置當前文件偏移量到之前的位置。
3 格式化輸入輸出
格式化輸出函數
有五個printf函數負責格式化輸出。
函數聲明:
#include
int printf(const char *restrict format, ...);
int fprintf(FILE *restrict fp, const char *restrict format, ...);
int dprintf(int fd, const char *restrict format, ..);
// All three return : number of characters output if OK , negative value if output error
int sprintf(char *resrict buf, const char *restrict format, ...);
// Returns: number of characters stored in array if OK, negative value if encoding error
int snprintf(char *restrict buf, size_t n, const char *restrict format, ...);
// Returns: number of characters,that would have been stored in array if buffer was large enough, negative value if encoding error
函數細節:
printf輸出到標準輸出;
fprintf輸出到指定的流中;
dprintf輸出到指定的文件描述符中;
sprintf將格式化字符串寫入到指定的buffer數組中,自動在結尾處加上一個null結尾符,但是不計入返回值中,并且,sprintf在buffer不夠大時可能發生越界,因此需要使用者保證buffer足夠大;
snprintf防止越界,在springf的參數中增加了buffer的大小參數,所有越界寫入的字符都被忽略,如果返回值比buffer得長度要小,則說明輸出沒有被截斷。
格式化輸入函數
函數聲明:
#include
int scanf(const char *restrict format, ...);
int fscanf(FILE *restrict fp, const char *restrict format, ...);
int sscanf(const char *restrict buf, const char *restrict format, ...);
函數細節:
format參數后面接得參數,包含存放讀入字符串的變量地址。
更多關于格式化輸入輸出的細節可以自己查詢Unix操作系統手冊。
4 從流中獲取文件描述符
函數聲明:
#include
int fileno(FILE* fp); ? ? ? // Returns: the file descriptor associated with the stream
如果我們需要調用dup和fcntl,則需要調用該函數。
5 臨時文件(Temporary Files)
標準IO庫提供了兩個函數用于創建臨時文件。
函數聲明:
#include
char* tempnam(char *ptr);
FILE* tmpfile(void);
函數細節:
函數tmpnam生成一個字符串,該字符串為一個合法的路徑名,并且不和任何已存在的文件重復。
函數tmpnam每次調用都生成不同的字符串,知道TMP_MAX次數。
如果函數tempnam的參數ptr為NULL,則生成的路徑字符串存在內存靜態區,函數返回值為指向該路徑字符串的指針。如果隨后再次使用null參數調用tempnam,會覆蓋之前生成的字符串。
如果函數tempnam的參數ptr不是NULL,那么生成的路徑字符串存在ptr指向的數組內,所以需要保證ptr指向的數組的長度至少為L_tmpnam。
函數tmpfile函數創建一個臨時二進制文件(type wb+),程序終止或者該文件被關閉,則該文件自動被刪除。對于UNIX操作系統而言,生成一個二進制文件并沒有什么影響,因為內核并不區分文本文件還是二進制文件。
Example:
Code:
#include?"apue.h"
int
main(void)
{
char? ? name[L_tmpnam], line[MAXLINE];
FILE? ? *fp;
printf("%s\n", tmpnam(NULL)); ? ? ? /* first temp name */
tmpnam(name); ? ? ? ? ? ? ? ? ? ? ? /* second temp name */
printf("%s\n", name);
if?((fp = tmpfile()) ==?NULL) ? ? ? /* create temp file */
err_sys("tmpfile error");
fputs("one line of output\n", fp);/* write to temp file */
rewind(fp); ? ? ? ? ? ? ? ? ? ? ? ? /* then read it back */
if?(fgets(line,?sizeof(line), fp) ==?NULL)
err_sys("fgets error");
fputs(line,?stdout);? ? ? ? ? ? ? ? /* print the line we wrote */
exit(0);
}
在系統The Single UNIX Specification定義了另外兩個函數處理臨時文件:
函數聲明:
char* mkdtemp(char* template); ? ?// Returns: pointer to directory name if OK, NULL on error
int mkstemp(char* template); ? ?// Returns: file descriptor if OK, -1 on error
函數細節:
mkdtemp函數創建一個名字唯一的文件夾
mkstemp函數創建一個名字唯一的常規文件(regular file)
命名規則為 template + 六位隨機字符
6 內存流(Memory Streams)
有的標準輸入輸出流并沒有對應打開的硬盤文件,所有操作都是與內存中buffer進行數據交換,這些流被叫做內存流(memory streams)。
函數聲明:
#include
FILE* fmemopen(void *restrict buf, size_t size, const char *restrict type);
// Returns: stream pointer if OK, NULL on error
函數細節:
參數buf指定使用的buffer,size為該buffer的大小,如果只指定size,而buf為null,那么fmemopen根據size的大小分配內存,由fmemopen分配的內存在流關閉時自動被釋放;
參數type控制該流的功能.
7 總結
標準IO函數庫被大多數UNIX應用使用。
在使用的時候,注意哪里使用了buffer來處理,因為這是容易引起迷惑的地方。
總結
以上是生活随笔為你收集整理的linux 流函数,标准IO函数库 - 二进制文件IO,流定位,创建临时文件和内存流的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OJ1089: 阶乘的最高位(C语言)
- 下一篇: 设计一个笔记本电脑类,属性随意,并且进行