日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

UNIX再学习 -- 标准I/O

發(fā)布時間:2025/3/15 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 UNIX再学习 -- 标准I/O 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

這部分之前有所總結(jié):

參看:C語言再學(xué)習(xí) -- 文件

參看:C語言再學(xué)習(xí) -- 輸入/輸出

參看:UNIX再學(xué)習(xí) -- 文件描述符

對比:UNIX再學(xué)習(xí) -- 文件I/O

一、流

文件I/O中所有的 I/O 函數(shù)都是圍繞文件描述符的。當(dāng)打開一個文件時,即返回一個文件描述符,然后該文件描述符就用于后續(xù)的 I/O 操作。 而對于標(biāo)準(zhǔn) I/O 庫,它們的操作是圍繞流(stream)進(jìn)行的。當(dāng)用標(biāo)準(zhǔn) I/O 庫打開或創(chuàng)建一個文件時,我們已使一個流與一個文件相關(guān)聯(lián)。 對于ASCII字符集,一個字符用一個字節(jié)表示,對于國際字符集,一個字符可用多個字節(jié)表示。標(biāo)準(zhǔn) I/O 文件流可用于單字節(jié)或多字節(jié)字符集。流的定向決定了所讀、寫的字符時單字節(jié)還是多字節(jié)。當(dāng)一個流最初被創(chuàng)建時,它并沒有定向。如若在未定向的流上使用一個多字節(jié) I/O 函數(shù),則將該流的定向設(shè)置為寬定向的。若在未定向的流上使用一個單字節(jié) I/O 函數(shù),則將該流的定向設(shè)為字節(jié)定向的。
只有兩個函數(shù)可以改變流的定向:freopen 函數(shù)清除一個流的定向;fwide 函數(shù)可用于設(shè)置流的定向。 下面介紹下 fwide 函數(shù): #include <wchar.h> int fwide(FILE *stream, int mode);

1、參數(shù)解析

第一個參數(shù):文件流 第二個參數(shù):mode 模式 根據(jù) mode 參數(shù)的不同值,fwide 函數(shù)執(zhí)行不同的工作。 如若 mode 參數(shù)值為負(fù),fwide 將試圖使指定的流是字節(jié)定向的。 如若 mode 參數(shù)值為正,fwide 將試圖使指定的流是寬定向的。 如若 mode 參數(shù)值為 0,fwide 將不試圖設(shè)置流的定向,但返回標(biāo)識該流定向的值。

2、函數(shù)功能

fwide 函數(shù)用于設(shè)置流的定向。
注意,fwide 并不改變已定向流的定向。還應(yīng)注意的是,fwide 無出錯返回。如若流是無效的,我么唯一可依靠的是,在調(diào)用 fwide 前先清除 errno,從 fwide 返回時檢查 errno 的值。?

3、返回值

若流是寬定向則返回正值,若是字節(jié)定向則返回負(fù)值,若是未定向的則返回 0。

4、示例說明

#include<stdio.h> #include <wchar.h> int main() {int res = fwide (stdin, 6);printf("%d\n",res);return 0; } 輸出結(jié)果: 1

二、FILE 對象

當(dāng)打開一個流時,標(biāo)準(zhǔn) I/O 函數(shù) fopen 返回一個指向 FILE 對象的指針該對象通常是一個結(jié)構(gòu),它包含了標(biāo)準(zhǔn) I/O 庫為管理該流需要的所有信息,包括用于實際 I/O 的文件描述符,指向用于該流緩沖區(qū)的指針、緩沖區(qū)的長度、當(dāng)前在緩沖區(qū)中的字符數(shù)以及出錯標(biāo)志等。 通過?/usr/include/libio.h?查看?C 語言中 _IO_FILE 結(jié)構(gòu)體的定義:?struct _IO_FILE { int _flags; /* High-order word is _IO_MAGIC; rest is flags. */ #define _IO_file_flags _flags /* The following pointers correspond to the C++ streambuf protocol. */ /* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */ char* _IO_read_ptr; /* Current read pointer */ char* _IO_read_end; /* End of get area. */ char* _IO_read_base; /* Start of putback+get area. */ char* _IO_write_base; /* Start of put area. */ char* _IO_write_ptr; /* Current put pointer. */ char* _IO_write_end; /* End of put area. */ char* _IO_buf_base; /* Start of reserve area. */ char* _IO_buf_end; /* End of reserve area. */ /* The following fields are used to support backing up and undo. */ char *_IO_save_base; /* Pointer to start of non-current get area. */ char *_IO_backup_base; /* Pointer to first valid character of backup area */ char *_IO_save_end; /* Pointer to end of non-current get area. */ struct _IO_marker *_markers; struct _IO_FILE *_chain; int _fileno; #if 0 int _blksize; #else int _flags2; #endif _IO_off_t _old_offset; /* This used to be _offset but it's too small. */ #define __HAVE_COLUMN /* temporary */ /* 1+column number of pbase(); 0 is unknown. */ unsigned short _cur_column; signed char _vtable_offset; char _shortbuf[1]; /* char* _save_gptr; char* _save_egptr; */ _IO_lock_t *_lock; #ifdef _IO_USE_OLD_IO_FILE }; ?/usr/include/stdio.h 也可以看到對標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出、標(biāo)準(zhǔn)錯誤的定義/* Standard streams. */ extern struct _IO_FILE *stdin; /* Standard input stream. */ extern struct _IO_FILE *stdout; /* Standard output stream. */ extern struct _IO_FILE *stderr; /* Standard error output stream. *//* C89/C99 say they're macros. Make them happy. */ #define stdin stdin #define stdout stdout #define stderr stderr

三、緩沖

標(biāo)準(zhǔn) I/O 庫提供緩沖的目的是盡可能減少使用 read 和 write 調(diào)用的次數(shù)。它也對每個 I/O 流自動地進(jìn)行緩沖管理,從而避免了應(yīng)用程序需要考慮這一點所帶來的麻煩。

1、標(biāo)準(zhǔn) I/O 提供了以下 3 種類型的緩沖。

(1)全緩沖

在這種情況下,在填滿標(biāo)準(zhǔn) I/O 緩沖區(qū)后才進(jìn)行實際 I/O 操作。對于駐留在磁盤上的文件通常是有標(biāo)準(zhǔn) I/O 庫實施全緩沖的。在一個流上執(zhí)行第一次 I/O 操作時,相關(guān)標(biāo)準(zhǔn)的 I/O 函數(shù)通常調(diào)用 malloc 獲取需使用的緩沖區(qū)。這部分可參看:C語言再學(xué)習(xí) -- 輸入/輸出? ?了解緩沖相關(guān)函數(shù)

(2)行緩沖

在這種情況下,當(dāng)在輸入和輸出中遇到換行符時,標(biāo)準(zhǔn) I/O 庫執(zhí)行 I/O 操作。這允許我們一次輸出一個字符,但只有寫了一行之后才進(jìn)行實際 I/O 操作。當(dāng)流涉及一個終端時(如標(biāo)準(zhǔn)輸入和標(biāo)準(zhǔn)輸出),通常使用行緩沖。
對于行緩沖有兩個限制:第一,因為標(biāo)準(zhǔn) I/O 庫用來收集每一行的緩沖區(qū)的長度是固定的,所以只要填滿了緩沖區(qū),那么即使還沒有寫一個換行符,也進(jìn)行 I/O 操作第二,任何時候只要通過標(biāo)準(zhǔn) I/O 庫要求從(a)一個不帶緩沖的流,或者(b)一個行緩沖的流得到輸入數(shù)據(jù),那么就會沖洗所有行緩沖輸出流

(3)不帶緩沖

標(biāo)準(zhǔn) I/O 庫不對字符進(jìn)行緩沖存儲。例如,標(biāo)準(zhǔn) I/O 函數(shù) fputs、標(biāo)準(zhǔn)錯誤流 stderr 通常是不帶緩沖的

2、ISO要求下列緩沖特征

(1)當(dāng)且僅當(dāng)標(biāo)準(zhǔn)輸入和標(biāo)準(zhǔn)輸出并不指向交互設(shè)備時,它們才是全緩沖的。(2)標(biāo)準(zhǔn)錯誤絕不會是全緩沖的(3)標(biāo)準(zhǔn)錯誤是不帶緩沖的(4)若是指向終端設(shè)備的流,則是行緩沖;否則是全緩沖的。

3、函數(shù) setbuf 和 setvbuf

參看:C語言再學(xué)習(xí) -- 輸入/輸出? 講的很詳細(xì)了,就不重復(fù)了。

四、打開流

#include <stdio.h> FILE *fopen(const char *path, const char *mode); FILE *fdopen(int fd, const char *mode); FILE *freopen(const char *path, const char *mode, FILE *stream);

1、參數(shù)解析

第一個參數(shù):文件路徑/文件描述符 第二個參數(shù):mode 方式

mode:C 字符串,包含了文件訪問模式,模式如下:

模式字符串

?

“r”

以只讀方式打開文件,該文件必須存在

“r+”

以只讀寫方式打開文件,該文件必須存在

“w”

打開只寫文件,若文件存在則文件長度清零,即該文件內(nèi)容會消失。

若文件不存在則建立該文件

“w+”

打開可讀寫文件,若文件存在則文件長度清零,即該文件內(nèi)容會消失。

若文件不存在則建立該文件。

“a”

以附加的方式打開只寫文件。若文件不存在,則會建立該文件,如果文件存在,

寫入的數(shù)據(jù)會被加到文件尾,即文件原先的內(nèi)容會被保留。(EOF符保留)

“a+”

以附加方式打開可讀寫的文件。若文件不存在,則會建立文件,如果文件存在,

寫入的數(shù)據(jù)會被加到文件尾后,即文件原先的內(nèi)容會被保留。(原來的EOF符不保留)

“rb”, “wb”, “ab”, “ab+”, “a+b”,

?“wb+”, “w+b”, “ab+”, “a+b”

與前面的模式相似,只是使用二進(jìn)制模式而非文本模式打開文件


2、函數(shù)功能

打開一個標(biāo)準(zhǔn) I/O 流。
這 3 個函數(shù)的區(qū)別如下: (1)fopen 函數(shù)打開路徑為 path 的一個指定的文件 (2)freopen 函數(shù)在一個指定的流上打開一個指定的文件,如若該流已經(jīng)打開,則先關(guān)閉該流若該流已經(jīng)定向,則使用 freopen 清除該定向。此函數(shù)一般用于將一個指定的文件打開為一個預(yù)定義的流:標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出、標(biāo)準(zhǔn)錯誤。 (3)fdopen 函數(shù)取一個已有的文件描述符(我們可能從 open,dup,dup2,fcntl,pipe,socket,socketpair或accept函數(shù)得到此文件描述符)?,并使一個標(biāo)準(zhǔn)的 I/O 流與該描述符相結(jié)合。此函數(shù)常用于創(chuàng)建管道和網(wǎng)絡(luò)通信通道函數(shù)返回的描述符。因為這些特殊類型的文件不能用標(biāo)準(zhǔn) I/O 函數(shù) fopen 打開,所以我們必須先調(diào)用設(shè)備專用函數(shù)以獲得一個文件描述符,然后用 fdopen 使一個標(biāo)準(zhǔn) I/O 流與該描述符相結(jié)合。

3、返回值

成功返回文件指針,失敗返回 NULL

4、示例說明

//示例一 fopen函數(shù) #include<stdio.h> #include <stdlib.h>int main (void) { FILE*fp = NULL; fp = fopen("abc.txt", "r"); if(NULL == fp) perror("fail to fopen"), exit (1); fclose (fp); fp = NULL; return 0; } //示例二 freopen 函數(shù) #include<stdio.h> #include <stdlib.h>int main (void) { FILE*fp = NULL; fp = freopen("abc.txt", "w", stdout); if(NULL == fp) perror("fail to freopen"), exit (1); printf ("hello world!\n");fclose (stdout);fclose (fp); fp = NULL; return 0; } 查看 abc.txt # cat abc.txt hello world!//示例三 fdopen 函數(shù) #include <stdio.h> #include <unistd.h>int main (void) {FILE * fp = fdopen (STDOUT_FILENO, "w+");fprintf (fp, "%s\n", "hello!");fclose(fp);return 0; } 輸出結(jié)果: hello!

5、示例總結(jié)

示例一:fopen 函數(shù)很好理解,沒什么可講的。 示例二:freopen 函數(shù),一般用于將指定的文件打開為一個預(yù)定義的流:標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出、標(biāo)準(zhǔn)錯誤。 示例三:fdopen 函數(shù),打開文件描述符。對于 fdopen 來說,mode 參數(shù)的意義稍有區(qū)別。因為該描述符已被打開,所以 fdopen 為寫而打開并不截斷該文件例如,若該描述符原來是由 open 函數(shù)創(chuàng)建的,而且該文件已經(jīng)存在,則其 O_TRUNC 標(biāo)志將決定是否截斷該文件。 另外,標(biāo)準(zhǔn) I/O 追加寫方式也不能用于創(chuàng)建該文件(因為如果一個描述符引用一個文件,則該文件一定已經(jīng)存在)。

6、擴(kuò)展部分

除非流引用終端設(shè)備,否則按系統(tǒng)默認(rèn),流被打開時是全緩沖的。若流引用終端設(shè)備,則該流是行緩沖的。一旦打開了流,那么在對該流執(zhí)行任何操作之前,如果希望,則可使用 setbuf 和 setvbuf 改變緩沖的類型 在該文件被關(guān)閉之前,沖洗(fflush )緩沖中的輸出數(shù)據(jù),緩沖區(qū)中的任何輸入數(shù)據(jù)被丟棄。如果標(biāo)準(zhǔn) I/O 庫已經(jīng)為該流自動分配了一個緩沖區(qū),則釋放此緩沖區(qū)。 當(dāng)一個進(jìn)程正常終止時(直接調(diào)用 exit 函數(shù),或從main 函數(shù)返回),則所有帶未寫緩沖數(shù)據(jù)的標(biāo)準(zhǔn) I/O 流都被沖洗,所有打開的標(biāo)準(zhǔn) I/O 流都被關(guān)閉 這部分在講 return 和 exit 區(qū)別是有講過,參看:C語言再學(xué)習(xí) -- 關(guān)鍵字return和exit ()函數(shù)

五、讀和寫流 (文件輸入/輸出)

一旦打開了流,則可在 3 種不同類型的非格式化 I/O 中進(jìn)行選擇,對其進(jìn)行讀、寫操作。 (1)每次一個字符的 I/O。 一次讀或?qū)懸粋€字符,如果流是帶緩沖的,則標(biāo)準(zhǔn) I/O 函數(shù)處理所有緩沖。 (2)每次一行的 I/O。 如果想要一次讀或?qū)懸恍?#xff0c;則使用 fgets 和 fputs。每行都以一個換行符終止。當(dāng)調(diào)用 fgets 時,應(yīng)說明能處理的最大行長。 (3)直接 I/O。 fread 和 fwrite 函數(shù)支持這種類型的 I/O。每次 I/O 操作讀或?qū)懩撤N數(shù)量的對象,而每個對象具有指定的長度。這兩個函數(shù)常用于從二進(jìn)制文件中每次讀或?qū)懸粋€結(jié)構(gòu)。

1、輸入函數(shù)

以下 3 個函數(shù)可用于一次讀一個字符。 #include <stdio.h> int fgetc(FILE *stream); int getc(FILE *stream); int getchar(void);

(1)返回值

?成功返回下一個字符,若已到達(dá)文件尾端或出錯,則返回 EOF

(2)函數(shù)比較

函數(shù) getchar 等同于 getc (stdin)。前兩個函數(shù)的區(qū)別是,getc 可被實現(xiàn)為宏,而 fgetc 不能實現(xiàn)為宏。這意味著以下幾點。 1) getc 的參數(shù)不應(yīng)當(dāng)是具有副作用的表達(dá)式,因為它可能會被計算多次。 2) 因為 fgetc 一定是個函數(shù),所以可以得到其地址。這就允許將 fgetc 的地址作為一個參數(shù)傳送給另一個函數(shù)。 3) 調(diào)用 fgetc 所需時間很可能比調(diào)用 getc 要長,因此調(diào)用函數(shù)所需的時間通常長于調(diào)用宏。 擴(kuò)展:有副作用的表達(dá)式,指的是表達(dá)式執(zhí)行后,會改變表達(dá)式中某些變量的值?
最簡單的如++i,這個表達(dá)式執(zhí)行后,i 的值會改變,這樣的表達(dá)式是不應(yīng)該在宏調(diào)用里出現(xiàn)的。

(3)示例說明

//示例一 fgetc 函數(shù) #include <stdio.h>int main (void) {FILE *fp;int c;fp = fopen ("abc.txt", "r");while ((c = fgetc (fp)) != EOF){if (c == 'b')fputc (c,stdout);}printf ("\n");fclose (fp);return 0; } 輸出結(jié)果: b//示例二 getc 函數(shù) #include<stdio.h> int main() { char c; printf("請輸入字符:"); c = getc(stdin); //等同函數(shù) getcharprintf("輸入的字符:"); putc(c, stdout); return(0); } 輸出結(jié)果: 請輸入字符:f 輸入的字符:f//示例三 getchar 函數(shù) #include <stdio.h> int main (void) { int c; while((c=getchar())!=EOF) putchar(c); return 0; }

(4)示例總結(jié)

這 3 個函數(shù)在返回下一個字符時,將其 unsigned char 類型轉(zhuǎn)換為 int 類型。說明為無符號的理由是,如果最高位為 1 也不會使返回值為負(fù)。要求整型返回值的理由是,這樣就可以返回所有可能的字符值再加上一個已出錯或已到達(dá)文件尾端的指示值。在 <stdio.h> 中常量 EOF 被要求是一個負(fù)值,其值經(jīng)常是 -1。這就意味著不能將這 3 個函數(shù)的返回值存放在一個字符變量中,以后還要將這些函數(shù)的返回值與常量 EOF 比較。 注意,不管是出錯還是到達(dá)文件尾端,這 3 個函數(shù)都返回同樣的值。為了區(qū)分這兩種不同的情況,必須調(diào)用 ferror 或 feof。 參看:C語言再學(xué)習(xí) -- EOF、feof函數(shù)、ferror函數(shù) 擴(kuò)展:ungetc ( ) 函數(shù) 從流中讀取數(shù)據(jù)以后,可以調(diào)用 ungetc 將字符再壓送回流中。

2、輸出函數(shù)

對應(yīng)于上面所述的每個輸入函數(shù)都有一個輸出函數(shù)。 #include <stdio.h> int fputc(int c, FILE *stream); int putc(int c, FILE *stream); int putchar(int c);

(1)函數(shù)比較

putchar函數(shù),輸出到顯示器

putc函數(shù),將字符輸入到文件

把stdout作為putc()函數(shù)的第二個參數(shù)。stdout是在stdout中定義的與標(biāo)準(zhǔn)輸出相關(guān)的文件指針,

所以putc (ch, stdout) 和 putchar ( )的作用是一樣的。

putc 可被實現(xiàn)為宏,而 fputc 不能實現(xiàn)為宏。

(2)示例說明 同上

六、每次一行 I/O (字符串輸入/輸出)

1、下面兩個函數(shù)提供了每次輸入一行的功能。

#include <stdio.h> char *fgets(char *s, int size, FILE *stream); char *gets(char *s);

(1)函數(shù)比較

這兩個函數(shù)都指定了緩沖區(qū)的地址,讀入的行將送入其中。gets 從標(biāo)準(zhǔn)輸入讀,而 fgets 則從指定的流讀對于 fgets 必須指定緩沖的長度 n。此函數(shù)一直讀到下一個換行符為止,但是不超過 n-1 個字符,讀入的字符被送入緩沖區(qū)。該緩沖區(qū)以 null 字節(jié)結(jié)尾。如若該行包括最后一個換行符的字符數(shù)超過 n-1,則 fgets 只返回一個不完整的行,但是,緩沖區(qū)總是以 null 字節(jié)結(jié)尾。對 fgets 的下一次調(diào)用會繼續(xù)讀該行。gets 是一個不推薦使用的函數(shù)。其問題是調(diào)用者在使用 gets 時不能指定緩沖區(qū)的長度。這樣就可能造成緩沖區(qū)溢出,寫到緩沖區(qū)之后的存儲空間中,從而產(chǎn)生不可預(yù)料的后果。

(2)示例說明

//示例一 fgets函數(shù) #include <stdio.h> int main (void) { char name [20]; char *ptr; ptr = fgets (name, 20, stdin); printf ("%s?, hi %s!\n", name, ptr); return 0; } 輸出結(jié)果: JOY JOY ?, hi JOY ! //示例二 gets 函數(shù) #include <stdio.h> int main (void) { char name[50]; char *ptr; while ((ptr = gets (name)) != NULL) { printf ("name is %s\n", name); printf ("ptr is %s\n", ptr); break; } return 0; } 輸出結(jié)果: HELLO name is HELLO ptr is HELLO

2、fputs 和 puts 提供每次輸出一行的功能

#include <stdio.h> int fputs(const char *s, FILE *stream); int puts(const char *s);

(1)函數(shù)比較

函數(shù) fputs 將一個以 null 字節(jié)終止的字符串寫到指定的流,尾端的終止符 null 不寫出。注意,這并不一定是每次輸出一行,因為字符串不需要換行符作為最后一個非 null 字節(jié)。通常,在 null 字節(jié)之前是一個換行符,但并不要求總是如此。puts 將一個 null 字節(jié)終止的字符串寫到標(biāo)準(zhǔn)輸出,終止符不寫出。但是,puts 隨后又將一個換行符寫到標(biāo)準(zhǔn)輸出。puts 并不像它所對應(yīng)的 gets 那樣不安全。但是我們還是應(yīng)避免使用它,以免需要記住它在最后是否添加了一個換行符。如果總是使用 fgets 和 fputs ,那么就會熟知在每行終止處我們必須自己處理換行符。

(2)示例說明

//示例一 fputs函數(shù) #include <stdio.h> int main (void) { char name [20]; fgets (name, 20, stdin); fputs (name, stdout);return 0; } 輸出結(jié)果: hello hello//示例二 puts函數(shù) #include <stdio.h> int main (void) { char name[50]; char *ptr; while ((ptr = gets (name)) != NULL) { puts (name);} return 0; }

七、二進(jìn)制 I/O

#include <stdio.h> size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream); size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream);

1、函數(shù)用法

這些函數(shù)有以下兩種常見的用法。

(1)讀或?qū)懸粋€二進(jìn)制數(shù)組。

例如:為了將一個字符數(shù)組的第 2~5 個元素寫到一文件上,可編寫如下程序://fwrite 函數(shù) #include <stdio.h> #include <stdlib.h>int main (void) {FILE *fp = fopen ("abc.dat", "wb"); char data[10] = "123456789";if (fwrite (&data[2], sizeof (char), 4, fp) != 4)perror ("fail to fwrite"), exit (1);return 0; } 查看 abc.data # cat abc.dat 3456//fread 函數(shù) #include <stdio.h> int main() {int arr[5]={},size=0,num=0; //不要老是打錯了好嗎,仔細(xì)仔細(xì)再仔細(xì)。FILE *p_file=fopen("a.bin","r");if(p_file){size=fread(arr,sizeof(int),5,p_file); //就是個數(shù)printf("size是%d\n",size);for(num=0;num<=4;num++){printf("%d ",arr[num]);}printf("\n");fclose(p_file);p_file=NULL;}return 0; }

(2)讀或?qū)懸粋€結(jié)構(gòu)。例如,可以編寫如下程序:

//fwrite 函數(shù) #include <stdio.h> #include <string.h> #include <stdlib.h>typedef struct { int n; float m; char name[20]; }Ptr; int main (void) { Ptr p;strcpy (p.name, "hello"); //注意字符串不能直接賦值 p.n = 11; p.m = 12.9; FILE *fp = fopen ("abc.dat", "wb");if (fwrite (&p, sizeof (Ptr), 1, fp) != 1)perror ("fail to fwrite"), exit (1);return 0; } //fread 函數(shù) #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct {int id;float salary;char name[20]; }person; int main() {person p={};FILE *p_file=fopen("person.bin","rb");if(p_file){while(1){if(!fread(&p,sizeof(person),1,p_file)){break;}fread(&p,sizeof(person),1,p_file);//二進(jìn)制寫操作printf("id是%d,工資是%g,姓名是%s\n",p.id,p.salary,p.name);}fclose(p_file);p_file=NULL;}return 0; }

2、使用二進(jìn)制 I/O 的基本問題

它只能用于讀在同一系統(tǒng)上已寫的數(shù)據(jù)。常常有這種情況,在一個系統(tǒng)上寫的數(shù)據(jù),要在另一個系統(tǒng)上進(jìn)行處理。在這種情況下,這兩個函數(shù)可能就不能正常工作,其原因是: (1)在一個結(jié)構(gòu)中,同一成員的偏移量可能因編譯器和系統(tǒng)而異。 (2)用來存儲多字節(jié)整數(shù)和浮點值的二進(jìn)制格式在不同的機(jī)器體系結(jié)構(gòu)間也可能不同。

八、定位流

有三種方法定位標(biāo)準(zhǔn) I/O。

1、ftell 和 fseek 函數(shù)。

#include <stdio.h> int fseek(FILE *stream, long offset, int whence); long ftell(FILE *stream); void rewind(FILE *stream); 它們都假定文件的位置可以存放在一個長整型中。參看:C語言再學(xué)習(xí) -- 文件? 在此不做重復(fù)。

2、ftello 和 fseeko 函數(shù)。

#include <stdio.h> int fseeko(FILE *stream, off_t offset, int whence); off_t ftello(FILE *stream); 可使文件偏移量不必一定使用長整型。它們使用 off_t 數(shù)據(jù)類型代替了長整型。 除了類型區(qū)別外,ftello 函數(shù) 與 ftell 相同,fseeko 函數(shù)和 fseek 函數(shù)相同。 off_t 類型我們前面有講到,linux中的 off_t 類型默認(rèn)是 32 位的 long int。

3、fgetpos 和 fsetpos 函數(shù)

#include <stdio.h>int fgetpos(FILE *stream, fpos_t *pos); int fsetpos(FILE *stream, fpos_t *pos); 返回值:成功返回0,失敗返回非0 它們使用一個抽象數(shù)據(jù)類型 fpos_t 記錄文件的位置。這兩個函數(shù)是由ISO C引入的。這種數(shù)據(jù)類型可以定義為記錄一個文件位置所需的長度。 fgetpos 將文件位置指示器的當(dāng)前值存入由 pos 指向的對象中在以后調(diào)用 fsetpos 時,可以使用此值將流重新定位至該位置。

(1)示例說明

#include <stdio.h> #include <string.h>int main (void) {FILE *fp;fpos_t filepos;fp = fopen ("abc.txt", "w+");fgetpos (fp, &filepos);fputs ("hello world!", fp);fsetpos (fp, &filepos);fputs ("這將覆蓋之前的內(nèi)容", fp);fclose (fp);return 0; } 查看 abc.ttx # cat abc.txt 這將覆蓋之前的內(nèi)容

(2)示例總結(jié)

首先我們使用 fgetpos() 函數(shù)獲取文件的初始位置,接著我們向文件寫入 Hello, World!,然后我們使用 fsetpos() 函數(shù)來重置寫指針到文件的開頭,重寫文件。

九、格式化 I/O

1、格式化輸出

格式化輸出是由 5 個printf 函數(shù)來處理的。 #include <stdio.h>int printf(const char *format, ...); int fprintf(FILE *stream, const char *format, ...); int dprintf(int fd, const char *format, ...); int sprintf(char *str, const char *format, ...); int snprintf(char *str, size_t size, const char *format, ...);

(1)函數(shù)比較

printf 將格式化數(shù)據(jù)寫到標(biāo)準(zhǔn)輸出; fprintf 寫至指定的流; dprintf 寫至指定的文件描述符; sprintf 將格式化的字符送入數(shù)組 buf 中。sprintf 在該數(shù)組的尾端自動加一個 null 字節(jié),但該字符不包括在返回值中 這部分之前也講過: 參看:C語言再學(xué)習(xí) -- 字符串和字符串函數(shù) 參看:C語言再學(xué)習(xí) -- 輸入/輸出 參看:C語言再學(xué)習(xí) -- printf、scanf占位符

(2)示例說明

//示例一 fprintf 函數(shù) #include <stdio.h> int main() { FILE *p_file = fopen("b.txt","w"); if(p_file) { //fprintf函數(shù)可以把數(shù)據(jù)按照格式記錄到文本文件中 fprintf(p_file,"%c,%g,%d\n",'c',3.14,46);fclose(p_file); p_file=NULL; } return 0; } 查看 b.txt # cat b.txt c,3.14,46//示例二 dprintf 函數(shù) #include <stdio.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <stdlib.h>int main (void) { int fd = open ("a.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);if (-1 == fd)perror ("fail to fopen"), exit (1);dprintf (fd, "%c, %g, %d\n", 'A', 5.12, 68);close (fd);return 0; } 輸出結(jié)果: # cat a.txt A, 5.12, 68//示例三 sprintf 函數(shù) #include <stdio.h> #define SIZE 30 int main (void) { char str[SIZE]; sprintf (str, "%s %s %d\n", "I","love",512 ); puts (str); return 0; } 輸出結(jié)果: I love 512

(3)示例總結(jié)

printf 函數(shù) 太簡單,這里就不寫例子了。

示例一:fprintf () 的工作方式和 printf() 相似,區(qū)別在于前者需要第一個參數(shù)來指定合適的文件流 示例二:dprintf 一樣和 printf 相似,區(qū)別在于前者需要第一個參數(shù)來指定合適的文件描述符 雖然 dprintf 不處理文件指針,但我們?nèi)匀话阉ㄔ谔幚砀袷交敵龅暮瘮?shù)中。注意,使用 dprintf 不需要調(diào)用 fdopen 將文件描述符轉(zhuǎn)換為文件指針(fprintf 需要)。
以上 3 個函數(shù),它們的返回值,成功返回輸出的字符個數(shù),失敗返回負(fù)值。

例:rv = printf ("hello"); ?結(jié)果為rv = 5;

再有,需要掌握的 printf( ) 格式轉(zhuǎn)換說明符,這里就不講了。


示例三:注意,sprintf 函數(shù)可能會造成由 buf 指向的緩沖區(qū)的溢出。調(diào)用者有責(zé)任確保該緩沖區(qū)足夠大。

溢出錯誤如下:

# ./a.out str = 12345678901234567890 len = 20 *** stack smashing detected ***: ./a.out terminated ======= Backtrace: ========= /lib/i386-linux-gnu/libc.so.6(__fortify_fail+0x45)[0xb7677dd5] /lib/i386-linux-gnu/libc.so.6(+0xffd8a)[0xb7677d8a] ./a.out[0x80484d5] /lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0xb75914d3] ./a.out[0x80483a1] ======= Memory map: ======== 08048000-08049000 r-xp 00000000 08:01 2102158 /home/tarena/project/c_test/a.out 08049000-0804a000 r--p 00000000 08:01 2102158 /home/tarena/project/c_test/a.out 0804a000-0804b000 rw-p 00001000 08:01 2102158 /home/tarena/project/c_test/a.out 094f1000-09512000 rw-p 00000000 00:00 0 [heap] b7547000-b7563000 r-xp 00000000 08:01 1704884 /lib/i386-linux-gnu/libgcc_s.so.1 b7563000-b7564000 r--p 0001b000 08:01 1704884 /lib/i386-linux-gnu/libgcc_s.so.1 b7564000-b7565000 rw-p 0001c000 08:01 1704884 /lib/i386-linux-gnu/libgcc_s.so.1 b7577000-b7578000 rw-p 00000000 00:00 0 b7578000-b7717000 r-xp 00000000 08:01 1704863 /lib/i386-linux-gnu/libc-2.15.so b7717000-b7719000 r--p 0019f000 08:01 1704863 /lib/i386-linux-gnu/libc-2.15.so b7719000-b771a000 rw-p 001a1000 08:01 1704863 /lib/i386-linux-gnu/libc-2.15.so b771a000-b771d000 rw-p 00000000 00:00 0 b772d000-b7731000 rw-p 00000000 00:00 0 b7731000-b7732000 r-xp 00000000 00:00 0 [vdso] b7732000-b7752000 r-xp 00000000 08:01 1704843 /lib/i386-linux-gnu/ld-2.15.so b7752000-b7753000 r--p 0001f000 08:01 1704843 /lib/i386-linux-gnu/ld-2.15.so b7753000-b7754000 rw-p 00020000 08:01 1704843 /lib/i386-linux-gnu/ld-2.15.so bf960000-bf981000 rw-p 00000000 00:00 0 [stack] 已放棄 (核心已轉(zhuǎn)儲)

因為緩沖區(qū)溢出會造成程序不穩(wěn)定甚至安全隱患,為了解決這種緩沖區(qū)溢出問題,引入了 snprintf 函數(shù)例如:

#include <stdio.h> #include <stdlib.h>int main (void) { char str[10] = {0};int len = snprintf (str, sizeof (str), "12345678901234567890");printf ("str = %s\n", str);printf ("len = %d\n", len);return 0; } 輸出結(jié)果: str = 123456789 len = 20 在該函數(shù)中,緩沖區(qū)長度是一個顯示參數(shù),超過緩沖區(qū)尾端的所有字符都被丟棄。如果緩沖區(qū)足夠大,snprintf 函數(shù)就會返回寫入緩沖區(qū)的字符數(shù)。與 sprintf 相同該返回值不包括結(jié)尾的 null 字節(jié)。

以上這 2 個函數(shù)的返回值,成功返回將要存入數(shù)組的字符數(shù),若編碼出錯,返回負(fù)值。

2、格式化輸入

執(zhí)行格式化輸入處理的是 3 個 scanf 函數(shù)。#include <stdio.h> int scanf(const char *format, ...); int fscanf(FILE *stream, const char *format, ...); int sscanf(const char *str, const char *format, ...); 3個函數(shù)返回值:賦值的輸入項數(shù);若輸入出錯或在任一轉(zhuǎn)換前已到達(dá)文件尾端,返回 EOF

(1)函數(shù)比較

scanf 族用于分析輸入字符串,并將字符序列轉(zhuǎn)換成指定類型的變量。在格式之后的各參數(shù)包含了變量的地址,用轉(zhuǎn)換結(jié)果對這些變量賦值。

(2)示例說明

//示例一 fscanf 函數(shù) #include <stdio.h> int main() { char ch=0; float fnum=0.0; int num=0; FILE *p_file=fopen("b.txt","r"); if(p_file) { //fscanf函數(shù)可以從文件中按照格式把數(shù)據(jù)拷貝到內(nèi)存的存儲區(qū)里 fscanf(p_file,"%c %g %d",&ch,&fnum,&num);//拷貝到存儲區(qū)我們就可以打印出來 printf("%c %g %d\n",ch, fnum, num); fclose(p_file); p_file=NULL; } return 0; } 輸出結(jié)果: c 3.14 46//示例二 sscanf 函數(shù) #include <stdio.h> #define SIZE 30 int main (void) { char str[SIZE]; sscanf ("12345", "%s", str);puts (str); return 0; } 輸出結(jié)果: 12345

(3)示例總結(jié)

示例一:fscanf 函數(shù)工作方式和 scanf 相似,區(qū)別在于前者需要第一個參數(shù)來指定合適的文件流
示例二:參看:C語言函數(shù)sscanf()的用法
sscanf 與 scanf 類似,都是用于輸入的,只是后者以屏幕 (stdin) 為輸入源,前者以固定字符串為輸入源
需要掌握的 scanf() 格式轉(zhuǎn)換修飾符,這里就不再重復(fù)了。
最后,需要注意下這 3 個函數(shù)的返回值,成功輸入的項目個數(shù)巧用scanf()返回值:status = scanf ("%ld", &num); while (status == 1) {status = scanf ("%ld", &num); } /*當(dāng)輸入整數(shù)則執(zhí)行while循環(huán),例如輸入Q,則scanf返回值為0,循環(huán)終止*/ 也可用下列形式代替: while(scanf ("%ld", &num) == 1) {}

十、函數(shù) fileno

#include <stdio.h> int fileno(FILE *stream); 返回值:與該流相關(guān)聯(lián)的文件描述符

1、函數(shù)功能

每個標(biāo)準(zhǔn) I/O 流都有一個與其相關(guān)聯(lián)的文件描述符。可以對一個流調(diào)用 fileno 函數(shù)以獲得其描述符。在講文件描述符時講過了,參看:UNIX再學(xué)習(xí) -- 文件描述符

2、示例說明

//示例一 #include <stdio.h> int main(void) { FILE *fp; int fd; fp = fopen("/etc/passwd", "r"); fd = fileno(fp); printf("fd = %d\n", fd); fclose(fp); return 0; } 輸出結(jié)果: fd = 3 //示例二 #include <stdio.h> #include <unistd.h> #include <stdlib.h> int main (void) { int fd = fileno (stdout);int newfd = dup (fd); if (-1 == newfd) perror ("Fail to dup"), exit (-1); printf ("newfd = %d\n", newfd); write (newfd, "hello world\n", 12); return 0; } 輸出結(jié)果: newfd = 3 hello world

3、示例總結(jié)

示例一:?fileno 可以用來取得參數(shù) stream 指定的文件流所使用的文件描述符。示例二:如果要調(diào)用 dup 或 fcntl 等函數(shù),則需要此函數(shù)。

十一、未講部分

標(biāo)準(zhǔn) I/O 的效率格式化 I/O printf/scanf 族的變體臨時文件內(nèi)存流標(biāo)準(zhǔn) I/O 的替代軟件

總結(jié)

以上是生活随笔為你收集整理的UNIX再学习 -- 标准I/O的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。