C語言的標準輸入輸出
歡迎探討,如有錯誤敬請指正
如需轉載,請註明出處 http://www.cnblogs.com/nullzx/
1. 標準輸入輸出
標準輸入、輸出主要由緩沖區和操作方法兩部分組。緩沖區實際上可以看做內存中的字符串數組,而操作方法主要是指printf、scanf、puts、gets,getcha、putcahr等操作緩沖區的方法。在C++以及Java等面向對象的編程語言中,將緩沖區以及操作緩沖區的方法封裝成一類對象,這類對象就稱為流。
緩沖區最大的特點主要體現在數據的一次性,即數據被printf、scanf從緩沖區中取出後就被使用了,或者說消耗了。可以把緩沖區比喻成管道,緩沖區中的數據比喻成水流,printf、scanf等方法比喻成開關,當打開開關,水就會慢慢流逝,而流出去的水就再也收不回來了。
由於不同系統,不的硬件底層實現輸入輸出的具體方法可能不一樣,C語言要求系統為每個程序提供兩個指針,這兩個指針分別指向兩個結構體,這兩個結構體分別表示了鍵盤和屏幕在內存中的抽象表示(緩沖區的地址值被記錄在這個結構體中),並將指向這兩個結構體的指針命名為stdin和 stdout.這兩個指針就是所謂的標準輸入和標準輸出。
還有一點應該始終銘記,標準輸入和輸出緩沖區中存儲的是字符的ASCII碼值。比如你想從鍵盤上輸入了123給一個變量,那麽在緩沖區中存儲是三個字節,分別是字符‘1’的ASCII碼值,字符‘2’的ASCII碼值,字符‘3’的ASCII碼值,然後將這個這三個ASCII值序列轉換為一個數值給這個變量。同理,從屏幕輸出“123”,計算機並不認為它輸出的是一個數值,計算機實際上僅僅是描繪了一個‘1’的ASCII碼值對應的圖形,‘2’的ASCII的值對應的圖形,‘3’的ASCII碼值對應的圖形。
2. getchar、putchar
putchar的作用主要是向輸出緩沖區中寫入一個字符。
getchar的作用主要是向輸入緩沖區中讀取一個字符。如果碰到文件結尾,返回-1
getchar源代碼
1 2 3 4 5 6 7 8 9 10 |
int getchar( void ){
static char buf[BUFSIZ];
static char * bb = buf;
static int n = 0 ;
if (n == 0 ) {
n = read( 0 , buf, BUFSIZ);
bb = buf;
}
return (--n >= 0 ) ? (unsigned char )*bb++ : EOF;
}
|
OEF是一個宏,表示-1。getchar的返回值是int,對於文件來說-1表示了文件的結尾。我們可以在鍵盤上利用Ctrl+Z來實現類似的效果。
從getchar的源代碼中可以看出,如果發現字符數組buf已空(n==0),則調用read方法從鍵盤讀取數據(該方法會導致阻塞),並讓指針指向數組的首地址。如果緩沖區還有字符沒有被讀取(n > 0),則讀取它,同時n-1,指針(bb)向後移動一位。當緩沖區已空(n==0),且read函數讀取失敗時(讀取到了文件末尾),返回EOF。
從scanf的源代碼中可以看出getchar可以讀入任何字符,包括空白符(空白符包括:空格、換行符、制表符等)。
3. gets、puts
puts函數主要向輸出緩沖區寫入一個字符串,並再字符串輸出結束以後,再額外輸出一個換行符 ‘\n‘。
gets用於從輸入流的緩沖區中讀取字符到指定的數組。讀取過程中會忽略所有的前導空白符,讀入的第一個字符為非空白符,直到遇到換行符才停止讀入,結束的換行符(‘\n‘)被gets函數讀從緩沖區讀取走了,存於數組中,然後被替換成‘\0‘。
gets 源代碼(只需要看for循環這部分代碼,FLOCKFILE(stdin)表示對輸入緩沖區加鎖對;FUNLOCKFILE(stdin) 表示對輸入緩沖區解鎖)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
char * gets ( char *buf){
int c;
char *s;
static int warned;
static const char w[] = "warning: this program uses gets(), which is unsafe.\n" ;
FLOCKFILE(stdin); ORIENT(stdin, -1);
if (!warned) {
( void )_write(STDERR_FILENO, w, sizeof (w) - 1);
warned = 1;
}
for (s = buf; (c = __sgetc(stdin)) != ‘\n‘ ;)
if (c == EOF)
if (s == buf) {
FUNLOCKFILE(stdin); return (NULL);
} else
break ;
else
*s++ = c; *s = 0;
FUNLOCKFILE(stdin); return (buf);
}
|
從源代碼可以看出,如果讀入了‘\n‘則停止,並替換成‘\0‘
4. printf的使用
定義函數 int printf(const char * format,...);
函數說明 printf()會根據參數format字符串來轉換並格式化數據,然後將結果寫出到標準輸出設備,直到出現字符串結束(‘\n‘)為止。
參數format字符串可包含下列三種字符類型:
(1)一般文本,伴隨直接輸出。
(2)轉義字符,如\t、\n等。
(3)格式轉換字符,格式轉換為一個百分比符號(%)及其後的格式字符所組成。一般而言,每個%符號在其後都必需有一printf的參數與之相呼應(只有當%%轉換字符出現時會直接輸出%字符)
格式轉換字符詳解 “%[符號][寬度][.精度]類型”
[寬度]:表示最少輸出的字符個數
[符號]:“-”表示對齊方式
(1)%-8,左對齊,當顯示字符不足8時,右補空格
(2)%08,右對齊,當顯示字符不足8時,左補0
[.精度]對於浮點數表示小數點後的位數
數值小數點後的位數大於顯示精度,則只能顯示[.精度]個小樹位數(四舍五入),如果數值小數點後的位數小於顯示精度,則補零。
%.5 小數點後顯示5位
類型:
(1)%d:用於顯示十進制有符號數,char,short,int,long long
(2)%u:用於顯示十進制無符號數,unsinged short,unsigned int,
unsigned long long
(3)%x: 用於顯示十六進制整數,所有有符號及無符號整型
(4)%f:用於顯示十進制浮點數,float,double
(5)%c:顯示字符
(6)%s:顯示字符串
printf(“%s”,xxx)與puts(xxx)的區別:puts函數會自動添加換行,而printf(“%s”,……)不會。
5. scanf的使用
定義函數 int scanf(const char * format,...);
函數說明 scanf()會將輸入的數據根據參數format字符串來轉換並格式化數據。Scanf()格式轉換的一般形式如下:
“%[寬度][數據所占字節數]輸入類型”
[寬度]:最多輸入的字符個數
[數據類型]:
h表示兩字節,short
l表示八字節,用於long long和 double
什麽都沒有表示四字節
[數據類型]輸入類型
(1)%d:int
(2)%f:float
(3)%lf:double
(4)%hd:short
scanf(“%c”,&x) 等價於 x = getchar(),雖然getchar的返回值是int類型,但不影響使用
(5)%s:字符串
用scanf讀取字符串時,忽略前導的空白符,再次遇到空白符會結束輸入,並將再次遇到的空白符留在緩沖區內,自動添加字符串數組的結束標誌‘\n‘。
1 2 3 4 5 6 7 8 |
#include <stdio.h>
void main( int argc, char * argv[]){
char a[20]; int ch; scanf_s( "%s" ,a,20);
printf ( "%s\n" , a);
while ((ch = getchar ()) != EOF ){
putchar (ch);
}
}
|
我們輸入i love you(ctrl+z)
scanf_s 讀取字符‘i‘以後結束(i後是空格),通過getchar函數第一個讀取的字符就是空格,getchar會一直讀取緩沖區中,直到緩沖區為空。
6. fgets、fputs、fscanf、fprintf、fgetchar、fputchar
上述方法只是多了個參數FILE * stream,表示這時的輸入以stream指定的文件作為輸入或者輸出
char* fgets(char* _Buf, int _MaxCount, FILE* _File);
int fputs(const char * _Str,FILE* _File);
int fprintf(FILE* _File, const char * _Format, ...);
int fscanf(FILE* _File, const char * _Format, ...);
int fgetc (FILE* _File) ;
int fputc(int _Ch, FILE* _File);
7. 其它相關函數
int sprintf( char *_Dest, const char * format,...);
函數說明sprintf和printf函數很類似,printf是將結果寫入到標準輸出流中,而sprintf是將結果寫入到字符串數組_Dest中。返回值返回值返回參數str字符串長度,失敗則返回-1。
1 2 3 4 5 6 7 |
#include<stdio.h>
void main(){
char * a = "This is string A!" ;
char buf[80];
sprintf_s(buf, "begin %s end\n" , a);
printf ( "%s" ,buf);
}
|
int sscanf_s(const char * _Src, const char * _Format, ...);
sscanf函數與scanf類似,只不過scanf是從輸入流中讀取數據,而sscanf是從字符串數組_Src中讀取數據
1 2 3 4 5 6 7 |
#include<stdio.h>
void main( int argc, char * args[]){
int i; double n;
char str[20] = "123 3.1415" ;
sscanf_s(str, "%d%lf" , &i, &n);
printf ( "%d\n%f\n" , i, n);
}
|
設置流緩沖
int fflush(FILE* stream);
void setbuf(FILE* stream, char* buf);
int setvbuf(FILE* stream, char* buf, int mode);
數據總是先寫入(或者讀取)到流中,當緩沖區滿了後,在將其寫入到設備(或者獲取讀取到程序中),這樣的工作方式效率更高。但是有時候我們可能需要更快的相應速度,我們可以調用fflush方法來沖刷緩沖區,註意這裏沖刷的意思不是將緩沖區的內容刪除,而是將還未滿的緩沖區中的內容寫入到設備(或者讀取到程序中)。
setbuf中可以由參數buf自己設定緩沖區的位置和大小(大小由buf數組的大小決定)。
setvbuf中的第三個參數決mode定了緩沖區的緩沖類型。它由三種取值
_IOFBU:全滿緩沖類型
_IOLBU:行滿緩沖類型
_IONBU:無緩沖類型
C語言的標準輸入輸出