寬定向,則代表從流中讀,寫的字符是多字節的.shell
字節定向,則代表從流中讀,寫的字符是單字節的.數組
當流被建立時,流的定向是未定義的,此時ide
在該流上使用一個多字節 IO 函數會將該流設置爲寬定向.函數
在該流上使用一個單字節 IO 函數會將該流設置爲字節定向.ui
當流用於輸入時,只存在2種緩衝類型:spa
無緩衝.則此時一次讀取一個字符.3d
有緩衝,則此時一次讀取緩衝區長度個字符,指針
int main(int argc,char *argv[]){ // setvbuf(stdin,0,_IONBF,0); char buf[128]; fgets(buf,sizeof(buf),stdin); } /* 註釋 setvbuf() 所在行,此時輸入流使用緩衝. */ $ strace ./Debug/Test 2 > sys_nobuf.log HelloWorld # 這個是輸入. $ cat sys_nobuf.log read(0, "HelloWorld\n", 1024) = 11 /* 此時一次讀取 1024 個字符 */ /* 取消對 setvbuf() 所在行的註釋 */ $ strace ./Debug/Test 2 > sys_nobuf.log HelloWorld # 這個是輸入. $ cat sys_nobuf.log # 由下能夠看出當輸入流不使用緩衝區時,一次讀取一個字符. read(0, "H", 1) = 1 read(0, "e", 1) = 1 read(0, "l", 1) = 1 read(0, "l", 1) = 1 read(0, "o", 1) = 1 read(0, "W", 1) = 1 read(0, "o", 1) = 1 read(0, "r", 1) = 1 read(0, "l", 1) = 1 read(0, "d", 1) = 1 read(0, "\n", 1) = 1
當流用於輸出時,有三種類型的緩衝:code
全緩衝,此時直至緩衝區滿時,纔會調用 write() 將整個緩衝區的內容送往內核.orm
行緩衝,此時直至緩衝區滿,或者輸出'\n'字符,或者須要調用 read() 從終端設備獲取數據時會經過 write() 調用將數據送往內核.
無緩衝,此時直接將輸出經過 write() 調用送往內核.
/* 當輸入流須要從終端獲取數據時,會刷新全部的行緩衝輸出流 */ int main(int argc,char *argv[]){ setvbuf(stdout,0,_IOLBF,0);/* 顯式將 stdout 設置爲行緩衝,由於若在 shell 中使用了重定向,則 stdout 可能會是全緩衝類型 */ char buf[4]; fputs("Hello",stdout); fgets(buf,sizeof(buf),stdin); fputs("World",stdout); fgets(buf,sizeof(buf),stdin); puts("Exit"); } $ strace ./Debug/Test 2>sys.log 1>/dev/null HelloWorld # 這個是輸入. $ cat sys.log write(1, "Hello", 5) = 5 read(0, "HelloWorld\n", 1024) = 11 write(1, "WorldExit\n", 10) = 10 # 由上輸出能夠看出在第一次調用 fgets() 時,須要從終端獲取數據,因此會刷新全部的行緩衝輸出流.即 write(1,"Hello",5) 調用在 read() 以前. # 由上輸出也能夠看出第2次 fgets() 調用並不須要從終端獲取數據,因此不會刷新行緩衝輸出流. $ cat t HelloWorld # 文件 t 的內容 $ strace ./Debug/Test 2>sys.log 1>/dev/null <t $ cat sys.log read(0, "HelloWorld\n", 4096) = 11 write(1, "HelloWorldExit\n", 15) = 15 # 此時並非從終端獲取數據,而是從文件中獲取輸入數據,因此不會刷新行緩衝輸出流,從上面 sys.log 的內容也能夠看出來.
int main(int argc,char *argv[]){ char buf[4]; fputs("Hello",stdout); fgets(buf,sizeof(buf),stdin); fputs("World",stdout); fgets(buf,sizeof(buf),stdin); puts("Exit"); } $ strace ./Debug/Test 2>sys.log 1>/dev/null # stdout 被重定向至文件,因此此時緩衝類型爲全緩衝. HelloWorld $ cat sys.log read(0, "HelloWorld\n", 1024) = 11 write(1, "HelloWorldExit\n", 15) = 15 # 以上輸出演示了從終端輸入數據並不會刷新全緩衝輸出流.
標準出錯輸出流 stderr,始終是無緩衝的.
若流與終端設備關聯(如:stdout,stdin),則默認是行緩衝的.不然默認是全緩衝的.
char *fgets(char *s, int size, FILE *stream);
該函數會從流 stream 中讀取數據,而後將數據複製到 buf 指向的緩衝區中,並追加'\0'字符,函數會一直讀取直至遇到如下三種狀況,纔會中止讀取:
讀取了 size-1 個字符.
遇到 EOF.
遇到了換行符,此時會將換行符也存放到緩衝區 buf 中.
返回值;若返回0,表示未讀取任何內容,此時多是由於遇到了錯誤,或者EOF;不然返回 s.
int printf(const char *format, ...);
format,格式字符串;在其中有2類字符: 普通字符,轉換指定串.
普通字符,會被直接複製到輸出流上.
轉換指定串,消耗0個或多個參數,將其轉換爲一個字符串,而後將該字符串複製到輸出流上,由'%'開始,由轉換指定符結尾,中間可能有(按照順序):
0個或多個標誌
一個可選的最小字段寬度,又稱'域寬',至關於一個文本框,好比若域寬爲7,能夠想象一個長度爲7個字符的文本框.
一個可選的精度,
一個可選的長度修飾符
#,在輸出中額外添加一些信息用來表示輸出的類型,如:
使用'#'狀況 | 示例 | |
o | 添加前綴'0',代表輸出是8進制 | printf("%o",12);// 輸出 '14' printf("%#o",12);// 輸出 '014' |
x,X | 添加前綴: '0x'(x),'0X'(X) | |
e,E,f,F,g,G,a,A | 總會加上小數點,不管小數點後有沒有數字 | |
g,G | 不會移除尾隨0 | printf("%g\n",3.33300000); /* 3.333 */ printf("%#g\n",3.33300000); /* 3.33300 */ |
0,指定了輸出應該對齊在域寬的右側,即右對齊;同時在左側補0以佔滿整個域寬.如:
printf("|%7d|\n",33); printf("|%07d|\n",33); /* --- 執行輸出 --- */ | 33| |0000033|
-,指定了輸出應該在域寬的左側對齊,默認右對齊,此時忽略'0'標誌,如:
printf("|%-7d|\n",33); printf("|%7d|\n",33); /* --- 執行輸出 --- */ |33 | | 33|
' '(空格),在正數(包括整數與浮點數)以前應該添加一個' '(空格),如:
printf("|%d|\n",33); printf("|% d|\n",33); /* 輸出 */ |33| | 33|
+,在正數(包括整數與浮點數)以前應該添加一個'+',如:
printf("|%d|\n",33); printf("|%+d|\n",33); /* --- 輸出 --- */ |33| |+33|
域寬的行爲:
當轉換後的字符串所含字符個數大於域寬時,域寬會自動擴充.
當轉換後的字符串所含字符個數小於域寬時,默認行爲: 字符串位於域寬的右側,而後以' '來填充左側.
經過'*'來指定域寬
若以'*'來指定域寬,代表域寬是由參數指定
若參數是一個負數,則至關於同時指定'-'標誌,而後用負數的絕對值來指定域寬
printf("|%7d|\n",3); printf("|%*d|\n",7,3); /* 經過'*'指定域寬 */ printf("|%-7d|\n",3); printf("|%*d|\n",-7,3); /* 參數爲負 */ /* 執行輸出 */ | 3| | 3| |3 | |3 |
在不一樣的轉換指定符下,精度具備不一樣的語義:
d,i,o,u,x,X 數字的最小位數,不足則填充0;如: printf("|%.7d|\n",3);/* |0000003| */
a,A,e,E,f,F 小數點後的數字位數
g,G 最大的有效數字,如: printf("%.5g",123.234334); // 123.23
s,S 容許輸出的最大字符個數,如: printf("%.3s","HelloWorld"); // Hel
精度的指定
以'.'開頭,緊隨若干個數字如:"%.33d";代表轉換的整數字符串最少要具備33個字符.
與最小字段寬度同樣,也能夠經過'*'來指定域寬;此時當參數爲負時,至關於未指定過精度,如:
printf("|%.7d|\n",3); /* |0000003| */ printf("|%.*d|\n",7,3); /* |0000003| */ printf("|%.*d|\n",-7,3); /* |3| */
進一步指定了參數的類型.如:
hh
對於: 'd,i,o,u,x,X'來講,代表參數類型爲unsigned char,或者char類型,而再也不是默認的int類型.
對於: n 來講,代表後續參數類型爲 singed char*;而再也不是默認的 int* 類型.
h,同 hh,只不過將 char 替換爲 short;
l
同 hh,只不過將 char 換成 long int;
對於 %c 來講表示後續參數的類型爲 wint_t;
對於 %s 來講表示後續參數的類型爲 wchar_t*;
ll,同 hh,只不過將 char 替換爲 long long int;
L,對於 'a,A,e,E,f,F,g,G' 來講,代表了參數類型爲 long double;
j,對於: 'd,i,o,u,x,X'來講,代表了參數類型爲 intmax_t, uintmax_t;
z,對於: 'd,i,o,u,x,X'來講,代表了參數類型爲 size_t,或者 ssize_t;
t,對於: 'd,i,o,u,x,X'來講,代表了參數類型爲 ptrdiff_t;
指定了參數的類型與參數應該被轉換爲何格式的字符串.
d,i 指定了參數類型爲int;應該被轉換爲十進制整數.
o,u,x,X 指定了參數類型爲 unsigned int;
o 指定了參數應該被轉換爲八進制
u 指定了參數應該被轉換爲10進制
x,X 指定了參數應該被轉換爲16進制,只不過'x'使用'abcdef',而'X'使用'ABCDEF'.
e,E 指定了參數類型爲 float/double,應該按照科學計數法轉換爲字符串,格式是這樣的:[-]d.ddde±dd;其中指數部分至少2個數字,如:
printf("%e\n",1.0); /* 1.000000e+00 */ printf("%E\n",1.0); /* 1.000000E+00 */
f,F 指定了參數類型爲 float/double,應該按照十進制計數法轉換爲字符串,格式: [-]ddd.ddd.
g,G 根據參數值來判斷是使用'f,F';或者'g,G'.
s 指定了參數類型爲 char*;若是指定了精度(而且小於字符數組的長度),則不須要'\0'做爲字符串的結尾,如:
char str[]={'I','L','Y'}; printf("%.3s",str); /* 此時不要求以'\0'結尾 */
c 指定了參數類型爲 unsigned char,而後寫入參數指定的字符(即該字符的 ASCII 碼值由參數指定).
p 指定了參數類型爲 void*,至關於'#lx'.
n 將目前已經寫至流的字符個數存放在一個 int* 參數.如: printf("HelloWorld%d%n",13,&i);// 則 i==12
m 將 strerror(errno) 寫道輸出流中,如: printf("%m");如: printf("%m"); /* Success *
int sscanf(const char *str, const char *format, ...);
空白字符,若一個字符使 isspace() 返回爲真,則該字符爲空白字符,如:'\n','\t',' '
format 是一個字符序列,其中字符能夠分爲三類:
空白字符,匹配輸入串 str 中0個或多個空白字符.
普通字符,必須匹配輸入串 str 中下一個字符.
轉換指定串,指定了匹配那些字符,以及將匹配得來的子串轉換爲什麼種數據類型.
char buf[]="He\n\t \t lloWorld"; char ch; sscanf(buf,"He l%cs",&ch); /* * 則匹配過程以下圖: * H,e 精確匹配. * 空白字符匹配0個或多個空白字符. * l 精確匹配. * %c 匹配任意字符. * s,o 匹配失敗,因此 sscanf() 返回. */
轉換指定串,指定了匹配那些字符,以及將匹配得來的子串轉換爲什麼種數據類型.其語法爲: %[*][域寬][m][類型修飾符]轉換指定符.其中'[]'表示可選.注意順序不要搞錯.
* 丟棄匹配得來的子串,此時不消耗參數.
char buf[]="123abcn" int i,j; sscanf(buf,"%*d%x",&i,&j); // %d 匹配得來的子串: 123;而後丟棄該子串,即並不進行轉換而後將整數 123 存入 i 中; // %x 匹配得來的子串: abc,將其轉換整數而後將結果 2748 存入 i 中.由於 * 不消耗參數,因此存入 i 中
域寬 指定了本次匹配的最大字符個數.
int i=33; char buf[]="1234567"; sscanf(buf,"%4d",&i);// 此時 %4d 匹配的子串爲 1234,並非 1234567.
m 僅當轉換指定符爲'c','s','[]'纔有效果,此時 sscanf() 內部會分配適當長度的內存塊,將匹配子串存入該內存塊中,而且將內存塊首地址賦值給指定的參數.如:
char buf[]="HelloWorld"; char *str=0; sscanf(buf,"%7ms",&str); // 7 指定了域寬,域寬與'm'的順序不可搞反. // 傳遞的是 &str,指向字符指針的指針. printf("%p: %s\n",str,str);
類型修飾符,用於進一步描述轉換指定符.
轉換指定符,用於指定匹配那些字符,以及將匹配子串轉換成何種數據類型.
類型修飾符 | 對應的轉化指定符 | 對應的指針類型 | ||||||
h | d,i,o,u,x,X,n | signed|unsigned short int * | ||||||
hh | 同 h | signed|unsigned char* | ||||||
j | 同 h | intmax_t* | uintmax_t *(8個字節的 int) | ||||||
l |
|
|
||||||
L |
|
|
||||||
t | 同 h | ptrdiff_t* | ||||||
z | 同 h | size_t* |
"%%"就是普通字符'%'
int a; scanf("%%%d",&a); /* 在數字以前必須有一個'%',如輸入"43",則因爲"%%"沒有找到匹配項'%'因此a不會被賦值 * 輸入"%43",則a成功輸入 */ printf("%d",a);
轉換指定字符 |
匹配的字符 |
對應的類型 |
d |
十進制有符號數字'[-0-9]' |
int |
i |
有符號整數,匹配的字符串相似"[-][0x|0]...",如"-0x33","-033"
|
int |
o |
[0-7];無符號八進制整數 |
unsigned int |
u |
[0-9];無符號十進制整數 |
unsigned int |
x|X |
[0-9a-z];無符號16進制數 |
unsigned int |
f |
有符號的浮點數 |
float |
s |
匹配不是空白字符的字符,默認跳過空白字符開始讀取,終止條件:at white space or at the maximum field width, whichever occurs first |
字符串 |
c |
匹配全部字符,終止條件:at the maximum field width(默認爲1,因此經常使用來讀取字符); |
字符數組(不會自動添加'\0') |
[] |
匹配[]指定的集合,終止條件:at the maximum field width 或者遇到不符合[]指定的字符集合; |
字符串 |
p |
匹配指針類型 |
void* |
n |
不是用來指示如何轉換字符串的,而是統計:the number of characters consumed thus far from the input(目前從輸入串中使用的字符數量,包括自動略過/主動略過的空白字符) |
int |
在使用'[]'時,因爲'-'與']'都是元字符,因此要想匹配'-'或者']';要把']'放在最開始的地方,'-'放在最後一個地方;如:'[]0-9-]用來匹配']','-','0-9';
%n 包括自動略過/主動略過的空白字符,%n 轉換並不計入 sscanf() 的返回值中!
int main(int argc,char *argv[]){ int num; int used_chars; int ret; ret=sscanf(argv[1],"%i%n",&num,&used_chars); PrintVar(num); PrintVar(used_chars); PrintVar(ret); } /* 執行輸出 */ $ ./Debug/Test ' 123 ' num: 123 used_chars: 6 /* 自動忽略的空格也被計入使用過的字符數中 */ ret: 1 /* %n 並不計入返回值. */ $ ./Debug/Test '123 ' num: 123 used_chars: 3 ret: 1