stdio.h

流的定向

  • 寬定向,則代表從流中讀,寫的字符是多字節的.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),則默認是行緩衝的.不然默認是全緩衝的.

fgets()

char *fgets(char *s, int size, FILE *stream);
  • 該函數會從流 stream 中讀取數據,而後將數據複製到 buf 指向的緩衝區中,並追加'\0'字符,函數會一直讀取直至遇到如下三種狀況,纔會中止讀取:

    • 讀取了 size-1 個字符.

    • 遇到 EOF.

    • 遇到了換行符,此時會將換行符也存放到緩衝區 buf 中.

  • 返回值;若返回0,表示未讀取任何內容,此時多是由於遇到了錯誤,或者EOF;不然返回 s.

printf()

int printf(const char *format, ...);

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&plusmn;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 *

sscanf()

int sscanf(const char *str, const char *format, ...);

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
d,i,o,u,x,X,n
e,f,g
c,s
unsigned|signed long int *
double *
wide-character(寬字符|寬字符串)
L
e,f,g
d,i,o,u,x
long double *
long long*
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"

  • 若是字符串以"0x"開頭,則匹配[0-9a-f],16進制

  • 若是字符串以'0'開頭,則匹配[0-7],8進制

  • 其它狀況下匹配[0-9],10進制

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
相關文章
相關標籤/搜索