2016年5月10日 星期二

給新手的C++教學 (上冊) - 8. 字元 & 字串 (Charactor & String)

回到「給新手的C++教學 (上冊)」

上一章

不知你是否嘗試過讓程式做到以下事情:
輸入一個名字 (大小寫字母組成),輸出「Hello, 名字!」

1. 土法煉鋼法

我們已經學過怎麼輸入一個字元 (甚麼是字元?),也知道怎麼用直接的方式 (用兩個「'」框起來) 表示一個字元
所以,我們可以一個一個的輸入字元,然後一一判斷是不是大小寫字母,當輸入的字元不是大小寫字母時,我們就知道一個「名字」已經輸入完了 (因為名字只能由大小寫字母組成)

套用上一章所學的簡化方式,我們就可以實現這個輸入名字的功能 (以下程式碼已經假設名字的長度$\leq$一百萬):

#include<cstdio>
int main()
{
    char name[1000001];
    int i=0;
    while(1==1)
    {
        scanf("%c",&name[i]);
        char c=name[i];
        if(c!='a'&&c!='b'&&c!='c'&&c!='d'&&c!='e'&&c!='f'&&c!='g'&&c!='h'&&c!='i'&&c!='j'&&c!='k'&&c!='l'&&c!='m'&&c!='n'&&c!='o'&&c!='p'&&c!='q'&&c!='r'&&c!='s'&&c!='t'&&c!='u'&&c!='v'&&c!='w'&&c!='x'&&c!='y'&&c!='z'&&c!='A'&&c!='B'&&c!='C'&&c!='D'&&c!='E'&&c!='F'&&c!='G'&&c!='H'&&c!='I'&&c!='J'&&c!='K'&&c!='L'&&c!='M'&&c!='N'&&c!='O'&&c!='P'&&c!='Q'&&c!='R'&&c!='S'&&c!='T'&&c!='U'&&c!='V'&&c!='W'&&c!='X'&&c!='Y'&&c!='Z')break;
        i=i+1;
    }
    printf("Hello, ");
    int j=0;
    while(j<i)
    {
        printf("%c",name[j]);
        j=j+1;
    }
    printf("!\n");
    return 0;
}

我才不信你會想這樣搞咧,對吧?
52個字母一一判斷實在是太累了,而且容易打錯

很幸運地,對電腦來說,「一個字元」其實就是「一個數字」
例如:「M」對應到「77」、「o」對應到「111」
如果你硬要把這些字元用整數的「%d」輸出 (而不是字元的「%c」),你看到的就是這些字元所對應到的數字
我用以下程式碼做示範:

#include<cstdio>
int main()
{
    printf("%d %d\n",'M','o');
}

沒意外的話,程式會輸出「77 111」

程式輸出「77 111」了!

因此,你甚至可以把每個直接表示的字元用數字來取代
把上面輸入一個名字的程式改寫成這樣:

#include<cstdio>
int main()
{
    char name[1000001];
    int i=0;
    while(1==1)
    {
        scanf("%c",&name[i]);
        char c=name[i];
        if(c!=97&&c!=98&&c!=99&&c!=100&&c!=101&&c!=102&&c!=103&&c!=104&&c!=105&&c!=106&&c!=107&&c!=108&&c!=109&&c!=110&&c!=111&&c!=112&&c!=113&&c!=114&&c!=115&&c!=116&&c!=117&&c!=118&&c!=119&&c!=120&&c!=121&&c!=122&&c!=65&&c!=66&&c!=67&&c!=68&&c!=69&&c!=70&&c!=71&&c!=72&&c!=73&&c!=74&&c!=75&&c!=76&&c!=77&&c!=78&&c!=79&&c!=80&&c!=81&&c!=82&&c!=83&&c!=84&&c!=85&&c!=86&&c!=87&&c!=88&&c!=89&&c!=90)break;
        i=i+1;
    }
    printf("Hello, ");
    int j=0;
    while(j<i)
    {
        printf("%c",name[j]);
        j=j+1;
    }
    printf("!\n");
    return 0;
}

測試看看,程式依然是同樣的邏輯!

輸入「Mobius」,程式依然是輸出「Hello, Mobius!」
(順帶一提,「Mobius」是我們「105級高雄中學科學班成果發表會的名稱,歡迎大家前去看看~~~)



2. 邏輯判斷法

聰明的你一定發現了
「'a'~'z'」對應到數字「97~122」
「'A'~'Z'」對應到數字「65~90」

那麼,我們只要和這些數字比大小,判斷有沒有在區間「97~122」或區間「65~90」就好啦!

所以我們輸入名字的程式碼就可以大幅精簡一個量級:

#include<cstdio>
int main()
{
    char name[1000001];
    int i=0;
    while(1==1)
    {
        scanf("%c",&name[i]);
        char c=name[i];
        if((97<=c&&c<=122)||(65<=c&&c<=90));
        else break;
        i=i+1;
    }
    printf("Hello, ");
    int j=0;
    while(j<i)
    {
        printf("%c",name[j]);
        j=j+1;
    }
    printf("!\n");
    return 0;
}

注意到程式碼中的「c」這個變數
「判斷c是字母」比「判斷c不是字母」還要來得容易
因此先判斷「判斷c是字母」
條件成立時代表甚麼事都不用做
條件不成立時就執行「else」裡面的程式碼 (break;) 跳出迴圈

此外,要判斷一個邏輯是不是「不成立」,有個簡單的方法
在前面加一個「!」:

#include<cstdio>
int main()
{
    char name[1000001];
    int i=0;
    while(1==1)
    {
        scanf("%c",&name[i]);
        char c=name[i];
        if(!((97<=c&&c<=122)||(65<=c&&c<=90))) break;
        i=i+1;
    }
    printf("Hello, ");
    int j=0;
    while(j<i)
    {
        printf("%c",name[j]);
        j=j+1;
    }
    printf("!\n");
    return 0;
}

注意到,我們要的是「判斷『(97<=c&&c<=122)||(65<=c&&c<=90)』是不是『不成立』」,而不是「判斷『(97<=c&&c<=122)』是不是『不成立』」
因此,我們需要用小括號將「(97<=c&&c<=122)||(65<=c&&c<=90)」包起來變成一體,才能放心的在前面加上「!」

此外,如果你不想記住「『a』對應到哪個數字」之類的
你大可直接把程式碼寫成這樣:

#include<cstdio>
int main()
{
    char name[1000001];
    int i=0;
    while(1==1)
    {
        scanf("%c",&name[i]);
        char c=name[i];
        if(!(('a'<=c&&c<='z')||('A'<=c&&c<='Z'))) break;
        i=i+1;
    }
    printf("Hello, ");
    int j=0;
    while(j<i)
    {
        printf("%c",name[j]);
        j=j+1;
    }
    printf("!\n");
    return 0;
}

趕快驗證一下,程式還是在做同樣一件事情,對吧?

3. 投機取巧法

咦?其實我們剛剛在做的事可以直接用兩行程式碼解決哦~ (遭踢飛)
第二章時我們學過:「int」要用「%d」,「char」要用「%c」,「float」要用「%f」
而在這一章,我們會學到:「字串」要用「%s」

甚麼是「字串」?就是一串字元
比較標準的說法,只要是一個或多個字元 (不一定要大小寫字母,甚至「' '」、「'\n'」也是可以接受的) 連在一起,我們就稱之為「字串」

當你用「scanf」來輸入一個「%s」時,程式得到的輸入會是一個「不含隱藏字元的字串」
「隱藏字元」泛指「無法直接顯示出來」的字元,例如:空白「' '」、Tab「'\t'」、換行「'\n'」

剛好,「大小寫字母」都不是隱藏字元 (大小寫字母顯示出來時你是看得到黑色部分的)
因此,不妨直接使用「%s」來輸入一個名字

剛剛說到,「%s」代表「不含隱藏字元的字串」,也就是「一串字元」,因此要用「char陣列」來儲存這「一串字元」
用法請參考以下程式碼:

#include<cstdio>
int main()
{
    char name[1000001];
    scanf("%s",name);
    printf("Hello, ");
    printf("%s",name);
    printf("!\n");
    return 0;
}

注意,用「scanf」輸入「%s」時,不需要在「char陣列名稱前面加「&」
然後,如果你想要達到「兩行程式碼」的目標,把三個「printf」寫成一個也無妨:

#include<cstdio>
int main()
{
    char name[1000001];
    scanf("%s",name);
    printf("Hello, %s!\n",name);
    return 0;
}

趕快驗證一下,程式還是在做同樣一件事情,對吧?
至此,你應該熟悉「%s」、字元、字串的處理和用法了
希望你程式的功能可以越來越強!

題外話:原本「土法煉鋼法」的程式碼裡面的那個「i」怎麼可以省略?(註4)

下一章

感謝:
(版權所有 All copyright reserved)

7 則留言:

  1. 回覆
    1. 您好,請問bug是什麼呢?
      小莫很樂意修正!
      感謝~

      刪除
    2. 那一整串的運算元都超出螢幕拉

      刪除
    3. 當然如果那是故意的就沒差就是了

      刪除
    4. 這個嘛...
      用Google Chrome的確會有這個問題 (如果用Edge瀏覽就沒事)
      剛剛針對這個問題上網研究了一下,還是找不到有用的解決方法
      抱歉哦...QQ

      刪除
  2. 嗨 百五的雄中學長,其實上面顯型字元,有包括數字
    ,所以應該還是有差啦 ,XD ~

    回覆刪除
    回覆
    1. 恩對呀XD
      那您有建議要怎麼修改嗎?

      刪除

歡迎留言或問問題~
若您的留言中包含程式碼,請參考這篇
如果留言不見了請別慌,那是因為被google誤判成垃圾留言,小莫會盡快將其手動還原