gcc中格式化輸出函數的研究

<一>

    1.前言

      在gcc編程中,我們比較經常用到的字符格式化輸出函數是printf的,實際上gcc繼承了c語言處理字符具有強大功能的風格,它提供了一系列的格式化輸出函數,主要存在兩個庫函數文件stdio.h/ stdarg.h中,具體函數如下:

    [code:1:cd7eec0dec]
    #include
    printf, int printf(const char *format, ...);
    fprintf, int fprintf(FILE *stream, const char *format, ...);
    sprintf, int sprintf(char *str, const char *format, ...);
    snprintf, int snprintf(char *str, size_t size, const char *format, ...);

    #include
    vprintf, vprintf(const char *format, va_list ap);
    vfprintf, vfprintf(FILE *stream, const char *format, va_list ap);
    vsprintf, int vsprintf(char *str, const char *format, va_list ap);
    [/code:1:cd7eec0dec]

      其中:

      printf和vprintf函數主要是向一個標準輸出設備或標準的字符流輸出格式化後的字符

      Fprintf和vfprintf 函數主要是向一個給定的字符流設備輸出格式化後的字符

      而sprintf, snprintf, vsprintf 和vsnprintf函數則是將格式化後的結果賦值給一個字符串

      雖然函數的功能上有差異,返回的值的類型也不盡相同,但是在建立匹配格式的語法上還是相同的,這幾個函數都有一些共同特徵,就是你要設計好模板和格式化的字串這些函數格式化字符串的命令主要是通過模板字符串中跟在“%”後面的字符來控制的

      如下一個例子中:
    [code:1:cd7eec0dec]
    int pct = 37;
    char filename[] = "foo.txt";
    printf ("Processing of `%s' is %d%% finished.Please be patient.",filename, pct);
    [/code:1:cd7eec0dec]

      顯然,這個例子的打印結果就是如下:

    Processing of `foo.txt' is 37% finished.Please be patient.

      如上例子我們可以看出一般格式化函數的語法特點就是包含格式化匹配的字符串,輸出的字串和變量組合的結構

    2.參數詳細介紹

      由於大部分函數在如何格式化字串部分的語法非常相似,我們先研究他們的共同特點,然後再應用不同的例子來分析其不同特點:此類函數一般的調用格式爲:printf("<格式化字符串>", <參量表>;

      其中格式化字符串包括兩部分內容: 一部分是正常字符, 這些字符將按原樣輸出; 另一部分是格式化規定字符, 以"%"開始, 後跟一個或幾個規定字符,用來確定輸出內容格式參量表是需要輸出的一系列參數, 其個數必須與格式化字符串所說明的輸出參數個數一樣多, 各參數之間用","分開, 且順序一一對應, 否則將會出現意想不到的錯誤

    2.1 Gcc提供的格式化規定符如下:

    %d
    十進制有符號整數

    %i
    十進制有符號整數

    注:上面這兩個函數在格式化輸出時用途是相同的,但在輸入時卻是不一樣的,%i可以接受任何形式的整數,而%d卻不能

    %u
    十進制無符號整數

    %f
    輸出浮點數

    %s
    輸出字符串

    %c
    輸出單個字符

    %p
    輸出指針的值

    %e %E
    指數形式的浮點數 ,其中:%e是以小寫形式輸出的 %E是以大寫形式輸出的

    %x, %X
    無符號以十六進制表示的整數,其中:%x是以小寫形式輸出的 %X是以大寫形式輸出的

    `%g', `%G'
    根據輸出數據的大小需要決定用普通形式還是指數形式的輸出方式,其中: %g是以小寫形式輸出的 %G是以大寫形式輸出的

    %o
    無符號以八進制表示的整數

    `%c
    '輸出單個字符

    %n
    得到輸出字符的個數,但是本參數不產生任何的輸出效果

    `%m'
    輸出錯誤時的相應的字符串提示

    `%%'
    輸出正文字符中的“%”字符

      說明:

    可以在"%"和字母之間插進數字表示最大場寬例如: %3d 表示輸出3位整型數, 不夠3位右對齊%9.2f 表示輸出場寬爲9的浮點數, 其中小數位爲2, 整數位爲6,小數點佔一位, 不夠9位右對齊%8s 表示輸出8個字符的字符串, 不夠8個字符右對齊如果字符串的長度、或整型數位數超過說明的場寬, 將按其實際長度輸出但對浮點數, 若整數部分位數超過了說明的整數位寬度, 將按實際整數位輸出;若小數部分位數超過了說明的小數位寬度, 則按說明的寬度以四捨五入輸出另外, 若想在輸出值前加一些0, 就應在場寬項前加個0例如: %04d 表示在輸出一個小於4位的數值時, 將在前面補0使其總寬度爲4位如果用浮點數表示字符或整型量的輸出格式, 小數點後的數字代表最大寬度,小數點前的數字代表最小寬度
    例如: %6.9s 表示顯示一個長度不小於6且不大於9的字符串若大於9, 則第9個字符以後的內容將被刪除

    可以在"%"和字母之間加小寫字母l, 表示輸出的是長型數
    例如: %ld 表示輸出long整數%lf 表示輸出double浮點數

    可以控制輸出左對齊或右對齊, 即在"%"和字母之間加入一個"-" 號可
    說明輸出爲左對齊, 否則爲右對齊例如: %-7d 表示輸出7位整數左對齊%-10s表示輸出10個字符左對齊

    2.2 一些特殊規定字符

    換行
    f 清屏並換頁
    回車
    Tab符
    xhh 表示一個ASCII碼用16進表示,其中hh是1到2個16進制數

     HopeCao 回覆於:2003-02-28 13:14:31
    <二>

    3.格式化轉換的具體細則

    3.1 整數轉換部分

      整數轉換部分主要是'%d', '%i', '%o', '%u', '%x', 和 '%X'這幾個參數命令的,由於參數的不同,可以輸出不同格式的結果如上表所列: '%d', '%i'是輸出一個帶符號的十進制的數,'%o', '%u', and '%x'是輸出一個不帶符號的數,而'%X是'%x''的大寫形式其中,針對這幾種不同輸出選擇還有如下幾個參數項:

    '-’ 表示是左對齊,一般都是右對齊的
    '+’ 是對'%d', '%i'兩個參數而言的,是指以'+’符號表示正數
    ' ' 是對'%d', '%i'兩個參數而言的,如果輸出不是以'+’'-’開頭的,那麼用空格做開頭
    '#' 是對'%o'參數而言的,將在輸出的結果強制加上'0’爲開頭
    ''' 將輸出的數字以LC_NUMERIC的分類法用’,’隔開
    '0' 將空格的地方用'0'填入
      如果沒有特別指明,被格式化的參數被默認當作整數處理,或者可以用以下的類型指定參數來進行修改,如下:

    'h' 指定傳入參數是 short int 或unsigned short int類型的
    'l' 指定傳入參數是 long int或unsigned long int類型的
    'q' 指定傳入參數是 long long int類型的
    'Z' 指定傳入參數是size_t.


      爲了方便理解給出一個例子:

    對於如下的格式化匹配字串:
    "|%5d|%-5d|%+5d|%+-5d|% 5d|%05d|%5.0d|%5.2d|%d|"

    將產生類似如下的輸出:

    | 0|0 | +0|+0 | 0|00000| | 00|0|
    | 1|1 | +1|+1 | 1|00001| 1| 01|1|
    | -1|-1 | -1|-1 | -1|-0001| -1| -01|-1|
    |100000|100000|+100000| 100000|100000|100000|100000|100000|

    對於如下的格式化匹配字串:
    "|%5u|%5o|%5x|%5X|%#5o|%#5x|%#5X|%#10.8x|"

    將產生類似如下的輸出:

    | 0| 0| 0| 0| 0| 0x0| 0X0|0x00000000|
    | 1| 1| 1| 1| 01| 0x1| 0X1|0x00000001|
    |100000|303240|186a0|186A0|0303240|0x186a0|0X186A0|0x000186a0|

    3.2 浮點數的轉換部分

      浮點數轉換部分主要是'%f', '%e', '%E', '%g', 和 '%G' '這幾個參數命令的,由於參數的不同,可以輸出不同格式的結果如上表所列: '%f'是輸出一個比較固定形式的浮點數……其中,針對這幾種不同輸出選擇還有如下幾個參數項:

    '-’ 表示是左對齊,一般都是右對齊的
    '+’ 是指以'+’符號表示正數
    ' ' 如果輸出不是以'+’'-’開頭的,那麼用空格做開頭
    '#' 是對'%g'和'%G’參數而言的,將在輸出的結果強制加上'0’爲開頭
    ''' 將輸出的數字以LC_NUMERIC的分類法用’,’隔開
    '0' 將空格的地方用“0'填入
      如果沒有特別指定,傳入的被格式化的參數默認是double類型的,可以用'L’表示是一個long double類型的

      如下例子可以看出浮點數格式化的字串:

      如下的格式字串:

    "|%12.4f|%12.4e|%12.4g|"

      可能產生如下的輸出:

    | 0.0000| 0.0000e+00| 0|
    | 1.0000| 1.0000e+00| 1|
    | -1.0000| -1.0000e+00| -1|
    | 100.0000| 1.0000e+02| 100|
    | 1000.0000| 1.0000e+03| 1000|
    | 10000.0000| 1.0000e+04| 1e+04|
    | 12345.0000| 1.2345e+04| 1.234e+04|
    | 100000.0000| 1.0000e+05| 1e+05|
    | 123456.0000| 1.2346e+05| 1.234e+05|

    3.3 其他格式的轉換部分

      這部分的函數比較簡單一些,具體如下:

    '%c’是指輸出一個單個的字符串,默認的輸出的被格式化的參數是unsigned char類型的,可以用'-’表示左對齊的沒有的別的參數,比如:
    printf ("%c%c%c%c%c", 'h', 'e', 'l', 'l', 'o');

    顯示結果爲: 'hello'

    '%s’是輸出一個字串,. 默認的輸出的被格式化的參數是char * (or const char *). 類型的,可以用'-’表示左對齊的沒有的別的參數,比如:
    printf ("%3s%-6s", "no", "where");

    顯示結果: ' nowhere '.

      注: 如果你用這個參數來格式化輸出一個指針類型的參數時,有可能會得到一個'(null)'的輸出值不過有時候用於指針爲空的緣故程序運行時會產生“Segmentation fault”的錯誤,下面一個例子就會產生這樣的錯誤:

    [code:1:2b36d1dc8e]
    #include
    main()
    {
    char a;
    a = inet_addr("192.168.1.1");
    if(a!=-1){
    printf("ip:%s",a);/* 這裏的%s可能會產生錯誤,應改用用%p比較好一些*/
    }
    }

    [/code:1:2b36d1dc8e]

    '%m’是輸出error信息的如下例子:
    fprintf (stderr, "can't open '%s': %m", filename);

    等於如下的輸出命令:

    fprintf (stderr, "can't open '%s': %s", filename, strerror (errno));

    “%p”是輸出指針類型參數的,顯然被格式化的輸入蠶室必須是指針,可以用“-”來表示左對齊的
    “%n”是比較特殊的參數,它不對格式化輸出影響,而是得到輸出結果的字符長度,可以用類型指定參數'h' 和 'l'來分別指定輸出的參數分別是short int *和 long int *類型的如下面的例子:
    int nchar;
    printf ("%d %s%n", 3, "bears", &nchar);

    輸出結果:

    3 bears

    同時將7的值賦給變量nchar

    '%%'是輸出“%”的字符


     HopeCao 回覆於:2003-02-28 13:19:11
    <三>

    4.函數具體介紹

    4.1printf()函數

      printf()函數是格式化輸出函數系列中比較有具有普遍特點的, 一般用於向標準輸出設備按規定格式輸出信息在編寫程序時經常會用到此函數printf()函數的調用格式爲:

      printf("<格式化字符串>", <參量表>;
    [code:1:7cfd895035]
    #include
    #include
    int main()
    {
    char c, s[20], *p;
    int a=1234, *i;
    float f=3.141592653589;
    double x=0.12345678987654321;
    p="How do you do";
    strcpy(s, "Hello, Comrade");
    *i=12;
    c='x41';
    printf("a=%d", a); /*結果輸出十進制整數a=1234*/
    printf("a=%6d", a); /*結果輸出6位十進制數a= 1234*/
    printf("a=%06d", a); /*結果輸出6位十進制數a=001234*/
    printf("a=%2d", a); /*a超過2位, 按實際值輸出a=1234*/
    printf("*i=%4d", *i); /*輸出4位十進制整數*i= 12*/
    printf("*i=%-4d", *i); /*輸出左對齊4位十進制整數*i=12*/
    printf("i=%p", i); /*輸出地址i=06E4*/
    printf("f=%f", f); /*輸出浮點數f=3.141593*/
    printf("f=6.4f", f); /*輸出6位其中小數點後4位的浮點數f=3.1416*/
    printf("x=%lf", x); /*輸出長浮點數x=0.123457*/
    printf("x=%18.16lf", x);/*輸出18位其中小數點後16位的長浮點數x=0.1234567898765432*/
    printf("c=%c", c); /*輸出字符c=A*/
    printf("c=%x", c); /*輸出字符的ASCII碼值c=41*/
    printf("s[]=%s", s); /*輸出數組字符串s[]=Hello, Comrade*/
    printf("s[]=%6.9s", s);/*輸出最多9個字符的字符串s[]=Hello,Co*/
    printf("s=%p", s); /*輸出數組字符串首字符地址s=FFBE*/
    printf("*p=%s", p); /* 輸出指針字符串p=How do you do*/
    printf("p=%p", p); /*輸出指針的值p=0194*/
    getch();
    retunr 0;
    }
    [/code:1:7cfd895035]

      上面結果中的地址值在不同計算機上可能不同

      例子中第一條語句#include的含義是調用另一個文件stdio.h, 這是一個頭文件, 其中包括全部標準輸入輸出庫函數的數據類型定義和函數說明對每個庫函數便用的變量及函數類型都已作了定義與說明, 放在相應頭文件"*.h"中, 用戶用到這些函數時必須要用#include<*.h>或#include"*.h" 語句調用相應的頭文件, 以供若沒有用此語句說明, 則連接時將會出現錯誤

    4.2 fprintf()函數

      fprintf(  函數中格式化的規定與printf(  函數相同, 所不同的只是fprintf()函數是向文件中寫入而printf()是向屏幕輸出

      下面介紹一個例子, 運行後產後一個test.dat的文件

    [code:1:7cfd895035]
    #include
    main()
    {
    char *s="That's good news"}; /*定義字符串指針並初始化*/
    int i=617; /*定義整型變量並初始化*/
    FILE *fp; /*定義文件指針*/
    fp=fopne("test.dat", "w"); /*建立一個文字文件只寫*/
    fputs("Your score of TOEFLis", fp);/*向所建文件寫入一串字符*/
    fputc(':', fp); /*向所建文件寫冒號:*/
    fprintf(fp, "%d", i); /*向所建文件寫一整型數*/
    fprintf(fp, "%s", s); /*向所建文件寫一字符串*/
    fclose(fp); /*關閉文件*/
    }
    [/code:1:7cfd895035]
      用CAT命令顯示TEST.DAT的內容如下所示:屏幕顯示

    Your score of TOEFL is: 617
    That's good news

    4.3 sprintf() 函數

      sprintf(string, fmt, ...)傳回的是string的類型的數組,並以空字符結尾不過,該函數有可能超過爲字符分配的長度比較危險下面是一個sprintf()的事例
    [code:1:7cfd895035]
    int
    //根據傳進來的Mission數據結構,建立socket鏈接,取得文件的大小
    get_size_of_url(struct Mission* pms)
    {
    int s;
    struct sockaddr_in sin;
    struct hostent* phe;
    char cmd[256];
    char msg_hdr[1000];
    char* p;
    //準備http中GET 方法的請求
    sprintf(cmd,"GET %s HTTP/1.0", pms->url);
    //創建socket
    if((s=socket(PF_INET,SOCK_STREAM,0))<0)
    return -1;
    //取得遠程主機的IP地址,失敗函數返回-1
    if((phe = gethostbyname(pms->host)) == NULL)
    return -1;
    memset(&sin,0,sizeof(sin));
    memcpy(&sin.sin_addr,phe->h_addr,sizeof(struct in_addr));
    sin.sin_family=AF_INET;
    sin.sin_port=htons(pms->port);
    //跟遠程機器建立連接,失敗函數返回-1
    if(connect(s,(struct sockaddr*)&sin,sizeof(sin))==-1)
    return -1;
    //發送GET請求
    if(write(s,cmd,strlen(cmd))<0)
    return 0;
    //從鏈接描述符(連接管道)中讀取傳送過來的數據
    if(read(s, msg_hdr, 300)<0)
    return 0;
    close(s);
    printf("%s",msg_hdr);
    //讀到該文件的大小
    if((p=strstr(msg_hdr,"Content-Length"))||(p=strstr(msg_hdr,"Content-length:")))
    p+=16;
    else
    return 0;
    //返回大小
    return atoi(p);
    }
    [/code:1:7cfd895035]
 


     注:在大部份的Unix系統上,sprintf(string, fmt, ...)傳回的是string的指標,然而,這方面Linux(遵循ANSI)傳回的卻是放入string內的字元數目.進行移植時,尤其是針對SunOS,需有警覺的心

    4.4 Snprintf()函數

      Snprintf()函數與Sprintf()函數極爲相似,但是該函數多了size參數來表示最大的字符數目,該函數返回一個整數值表示被存儲的字符的數目,如果返回-1則表示輸出的字符空間不夠如下例子:

    [code:1:7cfd895035]
    char *
    make_message (char *name, char *value)
    {
    /* 預分配100個字符空間. */
    int size = 100;
    char *buffer = (char *) xmalloc (size);
    while (1)
    {
    /* 輸出格式化的字符到給定的空間中. */
    int nchars = snprintf (buffer, size,"value of %s is %s",name, value);
    /* 判斷是否返回真值 */
    if (nchars < size)
    return buffer;
    /* 如果空間不夠,加大預分配空間到2倍 */
    size *= 2;
    buffer = (char *) xrealloc (size, buffer);
    }
    }
    [/code:1:7cfd895035]
    4.5 asprintf()函數

      int asprintf (char **ptr, const char *template, ...)

      本函數跟sprintf()函數很類似,只是它將字符串的分配改成動態分配的形式,參數ptr是指一個char *對象的地址函數返回指向一個新建的指針如下例子:

    [code:1:7cfd895035]
    /* Construct a message describing the value of a variable whose name is name and whose value is value. */
    char *
    make_message (char *name, char *value)
    {
    char *result;
    asprintf (&result, "value of %s is %s", name, value);
    return result;
    }
    [/code:1:7cfd895035]

    4.6 Vprintf()函數

      int vprintf (const char *template, va_list ap)

      本函數跟printf函數很類似,只是將參數的數目可變的,變成了一個指針的列表

    4.7 Vfprintf()函數

      int vfprintf (FILE *stream, const char *template, va_list ap)

      本函數跟fprintf函數很類似,只是將參數的數目可變的,變成了一個指針的列表

    4.8 vfprintf()函數

      int vsprintf (char *s, const char *template, va_list ap)

      本函數跟sprintf函數很類似,只是將參數的數目可變的,變成了一個指針的列表

    4.9 vsnprintf()函數

      int vsnprintf (char *s, size_t size, const char *template, va_list ap)

      本函數跟snprintf函數很類似,只是將參數的數目可變的,變成了一個指針的列表

    4.10 vasprintf()函數

      int vasprintf (char **ptr, const char *template, va_list ap)

      本函數跟asprintf函數很類似,只是將參數的數目可變的,變成了一個指針的列表

 

歡迎訪問:樂園www.ly8.co

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章