輸入輸出與格式化字串(C/C++)
標頭檔案<stdio.h>
C標準庫標頭檔案<stdio.h>中定義了三個變數型別、一些巨集和各種函式來執行輸入和輸出。
三個庫變數
下面是標頭檔案<stdio.h>中定義的變數型別:
序號 | 變數 & 描述 |
---|---|
1 | size_t 這是無符號整數型別,它是sizeof關鍵字的結果。 |
2 | FILE 這是一個適合儲存檔案流資訊的物件型別。 |
3 | fpos_t 這是一個適合儲存檔案中任何位置的物件型別。 |
庫巨集
下面是標頭檔案stdio.h中定義的巨集:
序號 | 巨集 & 描述 |
---|---|
1 | NULL 這個巨集是一個空指標常量的值。 |
2 | _IOFBF、_IOLBF和_IONBF 這些巨集擴充套件了帶有特定值的整型常量表達式,並適用於setvbuf函式的第三個引數。 |
3 | BUFSIZ 這個巨集是一個整數,該整數代表了setbuf函式使用的緩衝區大小。 |
4 | EOFM 這個巨集是一個表示已經到達檔案結束的負整數。 |
5 | FOPEN_MAX 這個巨集是一個整數,該整數代表了系統可以同時開啟的檔案數量。 |
6 | FILENAME_MAX 這個巨集是一個整數,該整數代表了字元陣列可以儲存的檔名的最大長度。如果實現沒有任何限制,則該值應為推薦的最大值。 |
7 | L_tmpnam 這個巨集是一個整數,該整數代表了字元陣列可以儲存的由 tmpnam 函式建立的臨時檔名的最大長度。 |
8 |
SEEK_CUR、SEEK_END和SEEK_SET 這些巨集是在These macros are used in thefseek函式中使用,用於在一個檔案中定位不同的位置。 |
9 | TMP_MAX 這個巨集是 tmpnam 函式可生成的獨特檔名的最大數量。 |
10 | stderr、stdin和stdout 這些巨集是指向 FILE 型別的指標,分別對應於標準錯誤、標準輸入和標準輸出流。 |
庫函式
下面是標頭檔案stdio.h中定義的函式:
序號 | 函式 & 描述 |
---|---|
1 | int fclose(FILEstream) 關閉流 stream。重新整理所有的緩衝區。 |
2 | void clearerr(FILE stream) 清除給定流 stream 的檔案結束和錯誤識別符號。 |
3 | int feof(FILEstream) 測試給定流 stream 的檔案結束識別符號。 |
4 | int ferror(FILE stream) 測試給定流 stream 的錯誤識別符號。 |
5 | int fflush(FILEstream) 重新整理流 stream 的輸出緩衝區。 |
6 | int fgetpos(FILE stream, fpos_tpos) 獲取流 stream 的當前檔案位置,並把它寫入到 pos。 |
7 | FILE fopen(const charfilename, const charmode) 使用給定的模式 mode 開啟 filename 所指向的檔案。 |
8 | size_t fread(voidptr, size_t size, size_t nmemb, FILEstream) 從給定流 stream 讀取資料到 ptr 所指向的陣列中。 |
9 | FILEfreopen(const charfilename, const charmode, FILEstream) 把一個新的檔名 filename 與給定的開啟的流 stream 關聯,同時關閉流中的舊檔案。 |
10 | int fseek(FILEstream, long int offset, int whence) 設定流 stream 的檔案位置為給定的偏移 offset,引數offset意味著從給定的whence位置查詢的位元組數。 |
11 | int fsetpos(FILE stream, const fpos_tpos) 設定給定流 stream 的檔案位置為給定的位置。引數pos是由函式 fgetpos 給定的位置。 |
12 | long int ftell(FILE stream) 返回給定流 stream 的當前檔案位置。 |
13 | size_t fwrite(const voidptr, size_t size, size_t nmemb, FILEstream) 把 ptr 所指向的陣列中的資料寫入到給定流 stream 中。 |
14 | int remove(const charfilename) 刪除給定的檔名 filename,以便它不再被訪問。 |
15 | int rename(const char old_filename, const charnew_filename) 把 old_filename 所指向的檔名改為 new_filename。 |
16 | void rewind(FILE stream) 設定檔案位置為給定流 stream 的檔案的開頭。 |
17 | void setbuf(FILEstream, charbuffer) 定義流 stream 應如何緩衝。 |
18 | int setvbuf(FILEstream, charbuffer, int mode, size_t size) 另一個定義流 stream 應如何緩衝的函式。 |
19 | FILEtmpfile(void) 以二進位制更新模式(wb+)建立臨時檔案。 |
20 | char tmpnam(charstr) 生成並返回一個有效的臨時檔名,該檔名之前是不存在的。 |
21 | int fprintf(FILE stream, const charformat, ...) 傳送格式化輸出到流 stream 中。 |
22 | int printf(const char format, ...) 傳送格式化輸出到標準輸出 stdout。 |
23 | int sprintf(charstr, const charformat, ...) 傳送格式化輸出到字串。 |
24 | int vfprintf(FILEstream, const charformat, va_list arg) 使用引數列表傳送格式化輸出到流 stream 中。 |
25 | int vprintf(const charformat, va_list arg) 使用引數列表傳送格式化輸出到標準輸出 stdout。 |
26 | int vsprintf(char str, const charformat, va_list arg) 使用引數列表傳送格式化輸出到字串。 |
27 | int fscanf(FILE stream, const charformat, ...) 從流 stream 讀取格式化輸入。 |
28 | int scanf(const char format, ...) 從標準輸入 stdin 讀取格式化輸入。 |
29 | int sscanf(const charstr, const charformat, ...) 從字串讀取格式化輸入。 |
30 | int fgetc(FILEstream) 從指定的流 stream 獲取下一個字元(一個無符號字元),並把位置識別符號往前移動。 |
31 | char fgets(charstr, int n, FILEstream) 從指定的流 stream 讀取一行,並把它儲存在 str 所指向的字串內。當讀取(n-1)個字元時,或者讀取到換行符時,或者到達檔案末尾時,它會停止,具體視情況而定。 |
32 | int fputc(int char, FILEstream) 把引數 char 指定的字元(一個無符號字元)寫入到指定的流 stream 中,並把位置識別符號往前移動。 |
33 | int fputs(const char str, FILEstream) 把字串寫入到指定的流 stream 中,但不包括空字元。 |
34 | int getc(FILE stream) 從指定的流 stream 獲取下一個字元(一個無符號字元),並把位置識別符號往前移動。 |
35 | int getchar(void) 從標準輸入 stdin 獲取一個字元(一個無符號字元)。 |
36 | chargets(charstr) 從標準輸入 stdin 讀取一行,並把它儲存在 str 所指向的字串中。當讀取到換行符時,或者到達檔案末尾時,它會停止,具體視情況而定。 |
37 | int putc(int char, FILEstream) 把引數 char 指定的字元(一個無符號字元)寫入到指定的流 stream 中,並把位置識別符號往前移動。 |
38 | int putchar(int char) 把引數 char 指定的字元(一個無符號字元)寫入到標準輸出 stdout 中。 |
39 | int puts(const char str) 把一個字串寫入到標準輸出 stdout,直到空字元,但不包括空字元。換行符會被追加到輸出中。 |
40 | int ungetc(int char, FILEstream) 把字元 char(一個無符號字元)推入到指定的流 stream 中,以便它是下一個被讀取到的字元。 |
41 | void perror(const char str) 把一個描述性錯誤訊息輸出到標準錯誤 stderr。首先輸出字串 str,後跟一個冒號,然後是一個空格。 |
輸入流
scanf
函式原型
int scanf(const char *format, ...);
描述
從標準輸入 stdin 讀取格式化輸入。
引數
- format-- 這是C字串,包含了以下各項中的一個或多個:空格字元、非空格字元和format說明符(format說明符將在下面單獨重點講解)。
- 附加引數-- 根據不同的format字串,函式可能需要一系列的附加引數,每個引數包含了一個要被插入的值,替換了format引數中指定的每個%標籤。引數的個數應與%標籤的個數相同。
返回值
如果成功,該函式返回成功匹配和賦值的個數。如果到達檔案末尾或發生讀錯誤,則返回EOF(-1)。
sscanf
函式原型
int sscanf(const char *str, const char *format, ...);
描述
從字串讀取格式化輸入。
引數
- str-- 這是C字串,是函式檢索資料的源。
- format-- 這是C字串,包含了以下各項中的一個或多個:空格字元、非空格字元和format說明符。
- 附加引數-- 這個函式接受一系列的指標作為附加引數,每一個指標都指向一個物件,物件型別由format字串中相應的 % 標籤指定,引數與 % 標籤的順序相同。
針對檢索資料的format字串中的每個format說明符,應指定一個附加引數。如果您想要把sscanf操作的結果儲存在一個普通的變數中,您應該在識別符號前放置引用運算子(&),例如:
int n; sscanf (str,"%d",&n);
返回值
如果成功,該函式返回成功匹配和賦值的個數。如果到達檔案末尾或發生讀錯誤,則返回EOF(-1)。
fscanf
函式原型
int fscanf(FILE *stream, const char *format, ...);
描述
從流stream讀取格式化輸入。
引數
- stream-- 這是指向FILE物件的指標,該FILE物件標識了流。
- format-- 這是C字串,包含了以下各項中的一個或多個:空格字元、非空格字元和format說明符。
- 附加引數-- 根據不同的format字串,函式可能需要一系列的附加引數,每個引數包含了一個要被插入的值,替換了format引數中指定的每個%標籤。引數的個數應與%標籤的個數相同。
返回值
如果成功,該函式返回成功匹配和賦值的個數。如果到達檔案末尾或發生讀錯誤,則返回EOF(-1)。
format說明符
又稱格式化字串,輸入流的format形式為%[*][width][modifiers]type],具體講解如下:
引數 | 描述 |
---|---|
* | 這是一個可選的星號,表示從流stream中讀取資料,但是忽略(跳過)它,即不將它儲存在引數中。 |
width | 指定了當前讀取操作中可讀取的最大字元數。 |
modifiers |
為對應的附加引數所指向的資料指定一個不同於整型、無符號整型或浮點型的大小。 h:短整型(針對d、i和n)或無符號短整型(針對o、u和x) l:長整型(針對d、i和n)或無符號長整型(針對o、u和x),或雙精度型(針對e、f和g) L:長雙精度型(針對e、f和g) |
type | 一個字元,指定了要被讀取的資料型別以及資料讀取方式,具體參見下一個表格。 |
scanf 型別說明符
型別 | 合格的輸入 | 引數型別 |
---|---|---|
c | 單個字元:讀取下一個字元。如果指定了一個不為1的寬度 width,函式會讀取width個字元,並通過引數傳遞,把它們儲存在陣列中連續位置。在末尾不會追加空字元。 | char |
d | 十進位制整數:數字前面的+或-號是可選的。 | int |
e,E,f,g,G | 浮點數:包含了一個小數點、一個可選的前置符號+或-、一個可選的後置字元e或E,以及一個十進位制數字。兩個有效的例項 -732.103和7.12e4 | float |
o | 八進位制整數。 | int |
s | 字串。這將讀取連續字元,直到遇到一個空格字元(空格字元可以是空白、換行和製表符)。 | char |
u | 無符號的十進位制整數。 | unsigned int |
x,X | 十六進位制整數。 | int * |
輸出流
printf
函式原型
int printf(const char *format, ...);
描述
傳送格式化輸出到標準輸出stdout。
引數
- format-- 這是字串,包含了要被寫入到標準輸出stdout的文字。它可以包含嵌入的format標籤,format標籤可被隨後的附加引數中指定的值替換,並按需求進行格式化(格式化字串format將在下面單獨重點講解)。
- 附加引數-- 根據不同的format字串,函式可能需要一系列的附加引數,每個引數包含了一個要被插入的值,替換了format引數中指定的每個 % 標籤。引數的個數應與 % 標籤的個數相同。
返回值
如果成功,則返回寫入的字元總個數,否則返回一個負數。
sprintf
函式原型
int sprintf(char *str, const char *format, ...);
描述
傳送格式化輸出到str所指向的字串。
引數
- str-- 這是指向一個字元陣列的指標,該陣列儲存了C字串。
- format-- 這是字串,包含了要被寫入到字串str的文字。它可以包含嵌入的format標籤,format標籤可被隨後的附加引數中指定的值替換,並按需求進行格式化。
- 附加引數-- 根據不同的format字串,函式可能需要一系列的附加引數,每個引數包含了一個要被插入的值,替換了format引數中指定的每個%標籤。引數的個數應與%標籤的個數相同。
返回值
如果成功,則返回寫入的字元總數,不包括字串追加在字串末尾的空字元。如果失敗,則返回一個負數。
fprintf
函式原型
int fprintf(FILE *stream, const char *format, ...);
描述
傳送格式化輸出到流stream中。
引數
- stream-- 這是指向FILE物件的指標,該FILE物件標識了流。
- format-- 這是C字串,包含了要被寫入到流stream中的文字。它可以包含嵌入的format標籤,format標籤可被隨後的附加引數中指定的值替換,並按需求進行格式化。
- 附加引數-- 根據不同的format字串,函式可能需要一系列的附加引數,每個引數包含了一個要被插入的值,替換了format引數中指定的每個%標籤。引數的個數應與%標籤的個數相同。
返回值
如果成功,則返回寫入的字元總個數,否則返回一個負數。
vprintf
函式原型
int vfprintf(FILE *stream, const char *format, va_list arg);
描述
使用引數列表傳送格式化輸出到流 stream 中。
引數
- stream-- 這是指向FILE物件的指標,該FILE物件標識了流。
- format-- 這是C字串,包含了要被寫入到流stream中的文字。它可以包含嵌入的format標籤,format標籤可被隨後的附加引數中指定的值替換,並按需求進行格式化。
- arg-- 一個表示可變引數列表的物件。這應被 中定義的va_start巨集初始化。
返回值
如果成功,則返回寫入的字元總數,否則返回一個負數。
vsprintf
函式原型
int vsprintf(char *str, const char *format, va_list arg);
描述
使用引數列表傳送格式化輸出到字串。
引數
- str-- 這是指向一個字元陣列的指標,該陣列儲存了C字串。
- format-- 這是字串,包含了要被寫入到字串str的文字。它可以包含嵌入的format標籤,format標籤可被隨後的附加引數中指定的值替換,並按需求進行格式化。
- arg-- 一個表示可變引數列表的物件。這應被 中定義的va_start巨集初始化。
返回值
如果成功,則返回寫入的字元總數,否則返回一個負數。
vfprintf
函式原型
int vfprintf(FILE *stream, const char *format, va_list arg);
描述
使用引數列表傳送格式化輸出到流stream中。
引數
- stream-- 這是指向 FILE 物件的指標,該 FILE 物件標識了流。
- format-- 這是 C 字串,包含了要被寫入到流 stream 中的文字。它可以包含嵌入的 format 標籤,format 標籤可被隨後的附加引數中指定的值替換,並按需求進行格式化。
- arg-- 一個表示可變引數列表的物件。這應被 中定義的va_start巨集初始化。
返回值
如果成功,則返回寫入的字元總數,否則返回一個負數。
printf中的格式化字串
輸出流以printf函式為基礎,有一系列功能各異的輸出函式(fprintf、sprintf、snprintf等),它們都在輸出時使用了強大的格式化字串。格式化字串以一個%開始,以型別欄位(d、f、x等)結束,完整格式如下:
%[引數][標誌][寬度][.精度][長度]型別
注意:除了%和型別之外均為可選欄位。
引數
引數欄位為POSIX擴充套件功能,非C99標準定義。使用m$指定格式化字串之後的第$m$個引數,從$14開始編號。假如使用了引數欄位,則所有的引數必須都至少使用一次,即從1$到n$都必須至少出現一次(其中n為格式化字串之後的引數個數),否則編譯時將產生如下警告:
warning: missing $ operand number in format [-Wformat=]
藉助引數欄位,可以實現引數的多次使用、亂序使用等,而不需要重複書寫引數。例如:printf("%2$d %2$#x; %1$d %1$#x",16,17);將輸出17 0x11; 16 0x10,兩個引數都使用了兩次,且先使用第二個引數(同時也使用了下一節的#標誌)。
標誌
- -:左對齊(預設為右對齊)
- +:給正數附加符號字首(預設為正數沒有任何字首)
- ``(空格):給正數附加空格字首(預設為正數沒有任何字首)
- 0:指定了寬度欄位(見下文)且為右對齊時,字首補0(預設補空格;當使用-標誌指定了左對齊時,0標誌失效)
- #:使用「備選格式」。對於g和G型別,不省略小數點部分最後的0;對於f、F、e、E、g、G型別,總是輸出小數點;對於o、x、X型別,分別在非零數值前附加0、0x、0X字首。上一節的例子中就使用了#標誌。
寬度
寬度欄位指定輸出字元的最小長度。長度不足的輸出將使用填充字元補齊,填充字元及對齊方式使用上述的0標誌和-標誌確定;超長的輸出不受影響(當然可使用下文的精度欄位限定)。
當指定寬度欄位時,可使用確定的整數值靜態指定,也可使用*號由某個引數動態指定。例如printf("%0*d", 5, 10);將輸出00010。注意:此例不能寫成%*0d即顛倒了標誌欄位和寬度欄位,否則將產生編譯警告:
warning: unknown conversion type character ‘0’ in format [-Wformat=] warning: too many arguments for format [-Wformat-extra-args]
精度
精度欄位指定輸出字元的最大長度。對浮點型資料來說,精度欄位指定了小數點後的最長有效位數;對字串來說,精度欄位指定了輸出的最大字元數。
與寬度欄位相同,指定精度欄位時,也可使用確定的整數值靜態確定或*號動態確定。為了與寬度欄位做區分,精度欄位前必須加句點。例如printf("%.*s", 3, "abcdef");將輸出abc。假如沒有句點,將被解析成寬度欄位(最小長度)為$3$而精度欄位(最大長度)未指定,因此將輸出abcdef。
長度
- hh: 用於將char型引數轉換成int型輸出。
- h: 用於將short 型引數轉換成int型輸出。
- l: 用於輸出long型引數。對於浮點型無效果。
- ll: 用於輸出long long型引數。
- L: 用於輸出long double型引數。
- z: 用於輸出size_t型引數。
- j: 用於輸出intmax_t型引數。
- t: 用於輸出ptrdiff_t型引數。
另有一些平臺特定的非標準長度字元,如I、I32、I64、q等,可參考維基百科printf format string詞條。
型別
- %: 原樣輸出一個%符號。此型別不接收任何其它欄位,即只能使用%。
- d, i: 輸出十進位制signed int型資料。二者僅在使用scanf輸入時有區別(使用%i將0x開頭的數解析為十六進位制,將0開頭的數解析為八進位制)。
- u: 輸出十進位制unsigned int型資料。
- f, F: 以定點數表示法輸出double型資料。二者區別在於無限小數和NaN輸出時是全小寫的inf、infinity、nan還是全大寫的INF、INFINITY、NAN。
- e, E: 以指數表示法輸出double型資料。二者區別在於字元e的大小寫。
- g, G: 根據指數自動選擇定點數表示法或指數表示法。二者區別同樣在於輸出字元的大小寫。以定點數表示法輸出時與f和F的區別在於,可能省略小數部分最後的0或小數點(資料為整數時)。
- x, X: 輸出十六進位制unsigned int型資料。二者區別在於十六進位制數的字元大小寫。
- o: 輸出八進位制unsigned int型資料。
- s: 輸出以\0結束的字串。
- c: 輸出一個char字元。
- p: 輸出void *,輸出格式信賴於具體實現。
- a, A: 輸出十六進位制double型資料,字首0x或0X。
- n: 將當前的格式化字串中已成功輸出的字元數寫入一個整型引數,字元數不包括\n、\t等轉義字元。從輸入輸出的方向來說,此型別使用時更像是在scanf中接受輸入,只不過輸入不是來自使用者、而是來自系統計數。注意:此計數僅對當前格式化字串有效,重新開始一個格式化字串時,計數將重置為0。例如printf("%n", &num);將把num賦值為0。
程式碼示例
可變寬度
可變寬度對於根據不同層次輸出不同的縮排有奇效,例如:
<kernel/resource.c> seq_printf(m, "%*s%0*llx-%0*llx : %s\n", depth * 2, "", width, start, width, end, r->name ? r->name : "<BAD>");
其中的%*s表示輸出depth * 2個空字元,字元個數是深度的兩倍;兩個%0*llx分別將start和end以十六進位制long long型輸出,最小寬度為width,寬度不足則以前導0補足。
十六進位制輸出
核心中大量使用了#標誌配合x型別輸出十六進位制數,隨便舉一個例子:
<mm/slab.c> seq_printf(m, "%s+%#lx/%#lx", name, offset, size);
其中的兩個%#lx即分別將offset和size以十六進位制long型輸出,並自動附加0x字首。注意:此例中的+號不是+標誌而是原樣輸出的字元,因為並不出現在%與型別欄位之間。
另外,核心的printk所使用的格式化字串有一些自定義的格式,可參考核心文件How to get printk format specifiers right。
(整理自網路)
參考資料:
https://wiki.jikexueyuan.com/project/c/c-standard-library-stdio-h.html
https://blog.lancitou.net/format-string-in-printf/