1. 程式人生 > 實用技巧 >輸入輸出與格式化字串(C/C++)

輸入輸出與格式化字串(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 ‘0in 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型引數。

另有一些平臺特定的非標準長度字元,如II32I64q等,可參考維基百科printf format string詞條。

型別

  • %: 原樣輸出一個%符號。此型別不接收任何其它欄位,即只能使用%
  • d, i: 輸出十進位制signed int型資料。二者僅在使用scanf輸入時有區別(使用%i將0x開頭的數解析為十六進位制,將0開頭的數解析為八進位制)。
  • u: 輸出十進位制unsigned int型資料。
  • f, F: 以定點數表示法輸出double型資料。二者區別在於無限小數和NaN輸出時是全小寫的infinfinitynan還是全大寫的INFINFINITYNAN
  • e, E: 以指數表示法輸出double型資料。二者區別在於字元e的大小寫。
  • g, G: 根據指數自動選擇定點數表示法或指數表示法。二者區別同樣在於輸出字元的大小寫。以定點數表示法輸出時與fF的區別在於,可能省略小數部分最後的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分別將startend以十六進位制long long型輸出,最小寬度為width,寬度不足則以前導0補足。

十六進位制輸出

核心中大量使用了#標誌配合x型別輸出十六進位制數,隨便舉一個例子:

<mm/slab.c>
    seq_printf(m, "%s+%#lx/%#lx", name, offset, size);

其中的兩個%#lx即分別將offsetsize以十六進位制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/