2016年9月16日 星期五

給新手的C++教學 (上冊) - 13 - 7. 中文字元字串的處理

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

回到「13. 額外語法 (Extra syntax)」

上一頁

先前有網友提問,他想寫一個程式:
輸入「是」會輸出「真是識相!\n」
輸入「不是」會輸出「你眼光有問題!\n」

我們先來看看這個程式的英文版本怎麼寫:
輸入「YES」會輸出「真是識相!\n」
輸入「NO」會輸出「你眼光有問題!\n」

#include<cstdio>
int main()
{
    printf("我是天才? YES or NO\n");
    char answer[4];
    scanf("%s",answer);
    if(answer[0]=='Y'&&answer[1]=='E'&&answer[2]=='S'&&answer[3]=='\0')
    {
        printf("真是識相!\n");
    }
    if(answer[0]=='N'&&answer[1]=='O'&&answer[2]=='\0')
    {
        printf("你眼光有問題!\n");
    }
    return 0;
}
輸入「YES」
輸入「NO」

請注意,「answer」的長度只有4,因此一旦輸入的長度超過3 (注意,還有一個表示字串結尾的「'\0'」) 就有可能導致程式出錯

那麼,要怎麼改成中文版本呢?是這樣嗎?

#include<cstdio>
int main()
{
    printf("我是天才? YES or NO\n");
    char answer[3];
    scanf("%s",answer);
    if(answer[0]=='是'&&answer[1]=='\0')
    {
        printf("真是識相!\n");
    }
    if(answer[0]=='不'&&answer[1]=='是'&&answer[2]=='\0')
    {
        printf("你眼光有問題!\n");
    }
    return 0;
}

很遺憾的,事情並沒有這麼簡單

中文字有一個問題:字母集合太龐大了 ($\geq 20000$種字)
單靠一個8位元的「char」(只能儲存$2^{8}=256$種資訊) 根本無法表示一個中文字!
因此實際上一個中文字會用「2個char」來表示 (2個char結合在一起可以表示$256^2=65536$種資訊)

也就是說,我們的程式架構應該改成這樣:

#include<cstdio>
int main()
{
    printf("我是天才? YES or NO\n");
    char answer[5];
    scanf("%s",answer);
    if(answer[0]==/*「是」的第1個char*/&&answer[1]==/*「是」的第2個char*/&&answer[2]=='\0')
    {
        printf("真是識相!\n");
    }
    if(answer[0]==/*「不」的第1個char*/&&answer[1]==/*「不」的第2個char*/&&answer[2]==/*「是」的第1個char*/&&answer[3]==/*「是」的第2個char*/&&answer[4]=='\0')
    {
        printf("你眼光有問題!\n");
    }
    return 0;
}

(請注意,上面的程式碼只是用來表示想法,並不能拿來執行)
要怎麼表示「『是』的第1個char」呢?
有個簡單的方法:
"是"[0]

用兩個「"」框起來的東西是一個「字串」,也就是「char陣列」,我們在「scanf」和「printf」裡面曾偷偷用過這個東西
因此,「"是"」就是一個長度為3的char陣列 (「是」這個中文字占2個char,還要加上一個「'\0'」來表示「字串結尾」)
於是
『「是」的第1個char』可以用「"是"[0]」來表示
『「是」的第2個char』可以用「"是"[1]」來表示

知道怎麼解決了嗎?
快來試試看吧!

#include<cstdio>
int main()
{
    printf("我是天才? YES or NO\n");
    char answer[5];
    scanf("%s",answer);
    if(answer[0]=="是"[0]&&answer[1]=="是"[1]&&answer[2]=='\0')
    {
        printf("真是識相!\n");
    }
    if(answer[0]=="不"[0]&&answer[1]=="不"[1]&&answer[2]=="是"[0]&&answer[3]=="是"[1]&&answer[4]=='\0')
    {
        printf("你眼光有問題!\n");
    }
    return 0;
}
輸入「是」
輸入「不是」

沒錯,問題解決了!

不過,有一件事想提醒「追求完美,近乎苛求」的同學們:
if(answer=="是") printf("真是識相!\n");」這種寫法,可能不是您想要的效果 (比較兩個字串有沒有相同)
這樣的程式碼,代表「判斷兩個char指標 (之前的頁面有提到『陣列就是指標』) 儲存的『記憶體位址』有沒有相等」
毫無意外,「answer=="是"」這樣的判斷結果永遠是「不成立」 (它們的記憶體位址怎麼可能相同嘛www)

下一頁

感謝:匿名網友
(版權所有 All copyright reserved)

6 則留言:

  1. 回覆
    1. 挖咧才剛寫完這篇就有神速回覆XD
      感謝,有空會繼續寫的~ :)

      刪除
  2. 這個網友......是Cody嗎(?)

    回覆刪除
    回覆
    1. 額...這個嘛...因為是匿名的所以不知道耶XD

      刪除
  3. 想問下關於表單的問題,如何製做不是DOS System的程式?

    回覆刪除
    回覆
    1. 小莫只知道Windows的做法耶抱歉
      每個作業系統的方式應該都不一樣,除非克難的自己寫UI

      刪除

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