C語言中文網 目錄

C語言格式化輸出

C 語言通過 printf()函數系列來格式化地輸出數據。本文采用相應的示例說明常用的格式化選項。

printf()函數系列

printf()函數以及多種它的相關函數都能夠提供數據的格式化輸出功能,它們通過使用格式化字符串(format string)作為函數參數來指定具體格式。然而,不同的函數具有不同的輸出目的,以及對所需輸出數據的訪問方法。下面的 printf()函數系列可用于處理字節導向流:
int printf(const char*restrict format,...);
寫入標準輸出流,stdout。

int fprintf(FILE*restrict fp,const char*restrict format,...);
寫入 fp 指定的輸出流。printf()函數可以視為 fprintf()的特殊版本。

int sprintf(char*restrict buf,
const char*restrict format,...);
將格式化數據寫入 buf 指向的 char 數組,并在后面加上一個標志結尾的空字符。

在上述函數原型中出現的省略號(...),表示還可有更多參數,但這些參數是可選的。還有一些 printf()函數系列需要一個指針參數,以指向一個參數列表,而不是在函數調用時直接接收數量可變的參數。這些函數的名稱都以一個 v 開始,表示“variable argument list”(可變參數列表)的意思:
int vprintf( const char * restrictformat, va_list argptr );
int vfprintf( FILE * restrict fp, const char * restrict format,
              va_list argptr );
int vsprintf( char * restrict buf, const char * restrict format,
              va_list argptr );
int vsnprintf( char * restrict buffer, size_t n,
               const char * restrict format, va_list argptr );

如果想使用支持可變參數列表的函數,除了頭文件 stdio.h 以外,還必須包含頭文件 stdarg.h。

上述函數都有相應的寬字符導向流版本。針對寬字符的 printf()函數名稱中包括字符串 wprintf 而不是 pintf,例如,vfwprintf()和 swprintf()等。但有一個例外:沒有 snwprintf()函數。而是采用 snprintf()對應到 swprintf(),該函數采用一個參數來指定最大輸出長度。

C11 標準為這些函數都提供了一個新的“安全”的版本。這些對應的新函數均以后綴 _s(例如,fprintf_s())。新函數測試它們接收的所有指針參數是否為空指針。

格式化字符串

格式化字符串是每個 printf()系列函數都具有的一個參數。格式化字符串定義了數據的輸出格式,并包含了一些普通字符和轉換說明(conversion specification)。每個轉換說明都定義了函數該如何將可選參數轉換并格式化,以供輸出。printf()函數將格式化字符串寫入到輸出,使用對應可選參數的格式化值來替代轉換說明。

轉換說明以百分號 % 開始,并以一個字母結尾,這稱為轉換修飾符(conversion specifier)。(為了在輸出中表示 %,需要一個特殊的轉換修飾符:%%。printf()將該符號轉換成一個單獨的百分號。)

轉換說明的語法以轉換修飾符作為結尾。在本文中,我們將使用這兩個術語來討論調用函數 printf()和 scanf()時所使用的格式化字符串。

轉換修飾符決定了轉換的類型,并且必須符合對應的可選參數。如下例所示:
int score = 120;
char player[ ] = "Mary";
printf( "%s has %d points.\n", player, score );

在調用 printf()時所使用的格式化字符串包含兩個轉換說明:%s 和 %d。對應的兩個可選參數也分別被指定:一個字符串,匹配轉換修飾符 s(表示“string”),以及一個 int 數值,匹配轉換修飾符 d(表示“decimal”)。示例中的函數調用,會在標準輸出設備中寫入下面的字符串:

Mary has 120 points.


所有的轉換說明(但 %% 是例外)都具有下面的通用格式:

%[標記][字段寬度][.精度][長度修飾符]修飾符


方括號內的這部分語法都是可選的,但是若要使用它們,就必須遵循上述次序。下面一節會詳細解釋每個參數類型合法的轉換說明。所有轉換說明都可包含“字段寬度”(field width)。然而,并非所有的轉換類型都有“精度”(precision)這個選項,對不同的修飾符來說,精度意義是不一樣的。

字段寬度

進行格式化的表格輸出時,字段寬度選項非常有用。如果包括該選項,字段寬度必須是正的十進制整數(或者是一個星號,下面會介紹)。字段寬度指定對應的數據項所輸出的最少字符數量。默認情況下,字段中的被轉換數據為右對齊(right-justified),左邊多的位置用空格填補。如果標記包含減號(-),則為左對齊(left-justified),超出的字段寬度采用空格向右填補。

下面的例子先輸出一行位置編號,然后展示字段寬度選項對輸出的作用:
printf("1234567890123456\n");           // 字符位置
printf( "%-10s %s\n", "Player", "Score" );      // 表頭
printf( "%-10s %4d\n", "John", 120 );   // 字段寬度:10;4
printf( "%-10s %4d\n", "Mary", 77 );

上述語句會生成一個簡單表格:

1234567890123456
Player     Score
John        120
Mary            77


如果輸出轉換的結果比所指定的寬度具有更多的字符,那么字段會做必要的擴充,以輸出完整的數據。

如果字段是右對齊的,可以采用 0 而非空格填充。要實現這樣的效果,在轉換說明標記中包括一個 0(指數字零)。下面的例子以 mm-dd-yyyy 的格式輸出日期:
int month = 5, day = 1, year = 1987;
printf( "Date of birth: %02d-%02d-%04d\n", month, day, year );

該 printf()調用會產生下面的輸出:

Date of birth: 05-01-1987


也可以使用一個變量來指定字段寬度。要實現這樣的效果,采用一個星號(*)作為轉換說明中的字段寬度,并在 printf()調用時包括一個額外的函數參數。該參數必須具有 int 類型,并且出現在需輸出的參數之前。如下例所示:
char str[ ] = "Variable field width";
int width = 30;
printf( "%-*s!\n", width, str );

上例中的 printf 語句在字段靠左邊位置輸出字符串 str,并且字段寬度由變量 width 決定。結果如下:

Variable field width         !


請注意輸出的最后有一個感嘆號(!)。感嘆號之前的一大段空格并非 str[] 在初始化時被賦值的內容。這些空格而是 printf 語句根據我們的要求為該字符串指定 30 個字符寬度而自動填充的。

輸出字符和字符串

printf()中針對字符串的轉換修飾符是 s,正如前面代碼中所示。針對字符的修飾符是 c(表示 char)。它們總結如表 1 所示。

表1 針對輸出字符和字符串的轉換修飾符
修飾符 參數類型 表示
c int 一個單獨的字符
s char 指針 該指針參數所指向的字符串

下面的例子在一個隊員名單中各成員之間輸出一個分隔字符:
char *team[ ] = { "Vivian", "Tim", "Frank", "Sally" };
char separator = ';';
for ( int i = 0; i < sizeof(team)/sizeof(char *); ++i )
  printf( "%10s%c ", team[i], separator );
putchar( '\n' );

用轉換說明 %c 表示的參數,可以擁有比 int 還小的類型(例如 char)。整數提升會自動地將該類型參數轉換成 int。然后函數 printf()將該 int 參數轉換為 unsigned char,并輸出對應的字符。

對于字符串輸出來說,可以指定能被輸出的最多字符數量。這時用到轉換說明的精度選項,精度表示為一個點后接一個十進制整數。如下例所示:
char msg[] = "Every solution breeds new problems.";
printf( "%.14s\n", msg );       // 精度:14
printf( "%20.14s\n", msg );     // 字段寬度是20;精度是14
printf( "%.8s\n", msg+6 );      // 從字符串msg的第7個字符起輸出字符串,精度為8

上述語句會產生下面的輸出結果:

Every solution
     Every solution
solution

輸出整數

函數 printf()可以把整數值轉換為十進制、八進位或十六進制表示。表 2 列出了用于格式化輸出整數的轉換修飾符。

表2 針對輸出整數的轉換修飾符
修飾符 參數類型 表示
d,i int 十進制
u unsigned int 十進制
o unsigned int 八進位
x unsigned int 十六進制,用小寫的 a、b、c、d、e、f
X unsigned int 十六進制,用大寫的 A、B、C、D、E、F

下面的例子展示同一個整數的不同轉換方式:
printf( "%4d %4o %4x %4X\n", 63, 63, 63, 63 );

該 printf()調用會產生下面的輸出:

63  77      3f      3F


修飾符 u、o、x 與 X 把對應的參數解釋為無符號整數。如果參數類型是 int,并且其值是負數,則轉換后輸出的是對應參數按照無符號整數解釋時其位模式下的正數值:
printf( "%d %u %X\n", -1, -1, -1 );

如果 int 為 32 位寬,那么該語句會產生下面的輸出:

-1       4294967295    FFFFFFFF


因為參數會受到整數提升的影響,同樣的轉換修飾符可以被用來格式化 short 和 unsigned short 參數。對于類型是 long 或 unsigned long 的參數,必須在 d、i、u、o、x 和 X 修飾符前面加上長度修飾符 l(小寫的 L)。類似地,如果參數是 long long 或 unsigned long long 類型,則其長度修飾符是 ll(兩個小寫 L)。如下例所示:
long bignumber = 100000L;
unsigned long long hugenumber = 100000ULL * 1000000ULL;
printf( "%ld   %llX\n", bignumber, hugenumber );

上述語句產生下面的輸出:

100000 2540BE400

輸出浮點數

表 3 列出了函數 printf()用來格式化輸出浮點數的轉換修飾符。

表3 針對輸出浮點數的轉換修飾符
修飾符 參數類型 表示
f double 十進制浮點數
e、E double 指數表示法,十進制
g、G double 浮點數或指數表示法,選擇其中較短者
a、A double 指數表示法,十六進制

最常用的修飾符是 f 和 e(或 E)。下面的例子展示了它們的用法:
double x = 12.34;
printf( "%f %e %E\n", x, x, x );

該 printf()調用將產生下面的輸出:

12.340000   1.234000e+01    1.234000E+01


在指數表示法中出現的 e 是大寫還是小寫,取決于函數轉換修飾符中所用 e 的大小寫。而且,如上例所示,默認輸出顯示精度為 6 位小數。轉換修飾符中的精度選項可修改這個默認設置:
double value = 8.765;
printf( "Value: %.2f\n", value );               // 精度為2:表示輸出為2位小數
printf( "Integer value:\n"
        " Rounded: %5.0f\n"                     // 字段寬度為5;精度為0
        " Truncated: %5d\n", value, (int)value );

該 printf()調用會產生下面的輸出:

Value: 8.77
Integer value:
 Rounded:               9
 Truncated:     8


正如上例所示,printf()會將浮點數按向上或向下取近似值,以便于輸出。如果指定精度為0,那么小數點本身則會被省略。如果僅僅想把小數部分直接去掉,而不是取近似值,直接將它轉換為整數類型可達到目的。

上述修飾符也可以配合 float 參數使用,因為 float 參數會自動地被提升為 double。但是,如想輸出類型為 long double 的參數,必須在轉換修飾符之前插入長度修飾符 L,如下例所示:
#include <math.h>
long double xxl = expl(1000);
printf( "e to the power of 1000 is %.2Le\n", xxl );

精美而實用的網站,提供C語言C++STLLinuxShellJavaGo語言等教程,以及socketGCCviSwing設計模式JSP等專題。

Copyright ?2011-2018 biancheng.net, 陜ICP備15000209號

底部Logo