天天看點

列印ASCII表

    • ASCII預備知識
    • 列印ASCII碼表
    • 最終的成果

到現在為止,我們已經學習了三種基本資料類型char/int/float、變量定義及指派、使用printf()函數進行輸出、條件表達式以及循環語句。基于這些基礎知識,我們已經完全有能力做點有意思的事情了,比如輸出ASCII表,同時更進一步探讨一些細節知識。

ASCII預備知識

ASCII碼使用指定的7位或8位二進制數組合來表示128或256種可能的字元。

标準ASCII 碼也叫基礎ASCII碼,使用7位二進制數(剩下的1位二進制為0)來表示所有的大寫和小寫字母,數字0 到9、标點符号, 以及在美式英語中使用的特殊控制字元。其中:

  • 0~31及127(共33個)是控制字元或通信專用字元(其餘為可顯示字元),如控制符:LF(換行)、CR(回-車)、FF(換頁)、DEL(删除)、BS(倒退)、BEL(響鈴)等;通信專用字元:SOH(文頭)、EOT(文尾)、ACK(确認)等;ASCII值為8、9、10 和13 分别轉換為倒退、制表、換行和回車字元。它們并沒有特定的圖形顯示,但會依不同的應用程式,而對文本顯示有不同的影響。
  • 32~126(共95個)是字元(32是空格),其中48~57為0到9十個阿拉伯數字。
  • 65~90為26個大寫英文字母,97~122号為26個小寫英文字母,其餘為一些标點符号、運算符号等。
  • 後128個稱為擴充ASCII碼。許多基于x86的系統都支援使用擴充(或“高”)ASCII。擴充ASCII 碼允許将每個字元的第8位用于确定附加的128個特殊符号字元、外來語字母和圖形符号。

列印ASCII碼表

由ASCII碼基礎知識我們可以得知,表中有128或256種可能的字元,這些編碼從0開始,其中存在一些控制字元,這些字元是不可列印的,也就是說使用printf()函數列印後在螢幕上不可見。另外後128個ASCII碼屬于擴充編碼,我們的程式先不予考慮,是以,我們最終隻輸出前128個編碼即可。面對同樣的事情做128次,首先循環語句上陣,下面這是最簡單的第一版。

#include <stdio.h>

int main()
{
    for ( int i = ; i < ; i +=  )
    {
        printf( "%c ", i );
    }

    printf( "\n" );
    return ;
}
           
列印ASCII表

這裡的for循環是從0開始(我們以前的練習是從1開始的),條件表達式為i<128,代表總共輸出128個字元。在循環體中我們以”%c”控制符列印數值對應的ASCII碼。程式運作結果圖如下,這看起來有些雜亂無章。此外,你也需要注意到這些情況:我們在循環體中并沒有輸出換行符,但第一行在輸出一些字元後換行了;我們無法有效的使用它查詢資訊,比如說字母’X’對應的數值是多少?也就是說程式沒有太多的實用性。最後,你可能會聽到“叮咚”一聲鈴響。我們先把程式改進一些,讓輸出變得美觀一些,然後再來解釋這些現象,下面是我們改進後的第2版。

#include <stdio.h>

int main()
{
    printf( "DEC\t HEX\t CHR\n" );
    printf( "--------------------\n" );

    for ( int i = ; i < ; i +=  )
    {
        printf( "%d\t %x\t %c\n", i, i, i );
    }

    return ;
}
           

這一版的程式中,我們首先使用printf()列印了一個表頭,該表格有三列,分别用于顯示目前數值的十進制,十六進制以及字元表示。我們使用\t水準制表符控制表格列之間的間距,\t後面多出一個空格,隻是為了使代碼看起來更清晰。在循環體中,由于printf()函數使用了三個控制字元,是以需要将目前數值傳值三次。看一下運作結果圖,感覺還不錯。你也留意到了在CHR一列,有些數值沒有對應的字元,這些可以稱為不可見字元。同時還要注意第10行與第11行之間多了一個空行,這是為什麼呢?

列印ASCII表

查閱一下ASCII碼表,我們會發現數值10代表換行控制符。當以%c格式符列印數值10時,相當于執行了一次換行操作,而在循環體中使用printf()輸出字元時又追了一個\n,是以中間多出了一個空行。同時ASCII碼值為7的控制字元代表響鈴,其轉義字元用’\a’表示,這就是你能聽到“叮咚”一聲響的原因。你可以使用下面兩行響鈴代碼測試一下。

// 響鈴: 雖然程式要求響鈴兩次,事實上隻會發生一次。
// 可以認為是輸出太快而響鈴太慢,當第一次響鈴時,兩次printf()已經完成。
printf( "\a" );
printf( "%c",  );
           

如果你想聽到兩次響鈴聲,可以在這兩行代碼中設定斷點,當第一個printf()執行後,響鈴開始,鈴聲結束後,再執行下一條語句就可以了。現在繼續回到主線任務,這一版的程式已經相當友好了,隻是假設控制台是一張紙,這樣的顯示有些太浪費了,畢竟右邊空白了那麼多,我們完全可以按兩列輸出。

#include <stdio.h>

int main()
{
    printf( "DEC\t HEX\t CHR\t\t DEC\t HEX\t CHR\n" );
    printf( "----------------------------------------------------\n" );

    for ( int i = ; i < ; i +=  )
    {
        printf( "%d\t %x\t %c\t\t ", i, i, i );
        printf( "%d\t %x\t %c\n", i+, i+, i+ );
    }

    printf( "\n" );
    return ;
}
           

看一下輸出效果,由于數值10按%c輸出時的緣故,程式進行了換行操作,導緻下一個輸出錯亂,這就是美中不足的地方。為此,我們可以使用if語句對數值10進行特殊處理,其它的保持不變。由于此時輸出了兩列,是以for語句中的變量遞增變成了i+=2,而非i+=1。

列印ASCII表

現在我們嘗試将其按三列輸出,看看還會有什麼問題。

#include <stdio.h>

int main()
{
    printf( "DEC\tHEX\tCHR\t   DEC\tHEX\tCHR\t   DEC\tHEX\tCHR\n" );
    printf( "--------------------------------------------------------------------\n" );

    for ( int i = ; i < ; i +=  )
    {
        printf( "%d\t%x\t%c\t   ", i,   i,   i   );
        printf( "%d\t%x\t%c\t   ", i+, i+, i+ );
        printf( "%d\t%x\t%c\n",    i+, i+, i+ );
    }
}
           
列印ASCII表

這次的輸出前半部分顯得太淩亂了!數值10/11/14并沒有與列起始處對齊,同時數值12消失了!13居然在14的後面輸出!先來解決10沒有對齊到列的問題。再次檢視一下ASCII碼表,數值9代表的正是控制字元\t,也就是說當程式按”%c”格式輸出數值9值,相當于增加了水準間距,這導緻後面10的輸出沒有對齊。

為了解決這個問題,我們在按”%c”輸出9時,把9替換為32,32對應的字元是空格,相當于消除的制表符自身的影響。同理,由于10的控制字元是\n,會引起換行操作,我們也将它替換空格字元。對于消失的數值12,相信你也有經驗了,它是控制符換頁的意思,我們也将它替換為空格即可。

最終的成果

#include <stdio.h>

int main()
{
    int i;
    int c1, c2;

    printf( "DEC\tHEX\tCHR\t   DEC\tHEX\tCHR\t   DEC\tHEX\tCHR\n" );
    printf( "--------------------------------------------------------------------\n" );

    for ( i = ; i < ; i +=  )
    {
        // 表格有列,是以每行開頭的索引為,,,....
        if ( i !=  && i !=  )
        {
            c1 = i;
            c2 = i + ;
        }
        else
        {
            if ( i ==  )
            {
                c1 = ; // 将制表符替換為空格
                c2 = ; // 将換行符替換為空格
            }
            else if ( i ==  )
            {
                c1 = ; // 将換頁符替換為空格
                c2 = ; // 将回車符替換為空格
            }
        }

        printf( "%d\t0x%x\t%c\t   ", i,   i,   c1  );
        printf( "%d\t0x%x\t%c\t   ", i+, i+, c2 );
        printf( "%d\t0x%x\t%c\n"   , i+, i+, i+ );
    }

    return ;
}
           

程式定義了c1/c2兩個變量用于特殊化第一列與第二列的控制字元的輸出。同時,對于第二列16進制值的輸出,加入了前導符0x标記。在循環體中,我們使用了if語句處理了上面分析中存在的特殊情況。第一個if語句描述了更多的通用情況,else子句則用于處理特殊情況。一般情況下更容易成立的條件優先處理是個較好的選擇。

列印ASCII表

這個程式是目前我們編寫的最複雜的程式,希望能達到鞏固知識的目的。相信你現在也已經迫不急待的想要嘗試一下。最後,你可以将控制台輸出拖動到最後一行,你會發現程式輸出了值為128的情況。等等,這是為什麼?我們在for循環中指定的判斷條件是i<128呀,嘗試自己思考一下這個問題的所在。

繼續閱讀