天花板編程手把手計(jì)劃-第1期-第6天

這一期的內(nèi)容已經(jīng)過(guò)半议街,部分同學(xué)開(kāi)始覺(jué)得吃力割捅。如果這時(shí)候放棄奶躯,那前邊的努力就白費(fèi)了。今天我們來(lái)看看上一篇中的課后題亿驾。

1. 題目

給出一組代數(shù)表達(dá)式嘹黔,請(qǐng)編程判斷出他們的括號(hào)是否配對(duì)正確。

10
[a+b)-(d+e]
(a+b))+((d-e)
(a+b)-[c+d
[a+(b+c)]*(a+b]
(a-1+b)-(b+c)
x-[a*(b+c))]
a+(b+c+(d-(e+m))
a+b+[c-d*e-(a-b)-c]
[a+b-d]+e)
[a-(b+)+[d-a]-b]

第一行中的10表示共有10個(gè)表達(dá)式需要判斷莫瞬。下面的每一行有一個(gè)表達(dá)式儡蔓。要求把這組數(shù)據(jù)原樣保存在文件input.txt中,通過(guò)讀文件的方式讀入數(shù)據(jù)完成判斷疼邀。

原題只有5個(gè)表達(dá)式喂江,這里我又加入了5個(gè)新的,有一些特殊的情況需要添加旁振。

2. 分析

這道題從功能上講需要做兩件事:

  • 第一件:把保存在input.txt文件中的數(shù)據(jù)讀入程序中
  • 第二件:掃描每行字符串获询,判斷是否所有的括號(hào)都匹配

是不是看起來(lái)沒(méi)那么復(fù)雜呢?下面我們就來(lái)看如何實(shí)現(xiàn)這兩個(gè)功能拐袜。

3. 讀數(shù)據(jù)

3.1 基本方法

說(shuō)到讀數(shù)據(jù)吉嚣,我們本能地想到C語(yǔ)言教科書(shū)中文件操作的那一章,用基本的打開(kāi)文件蹬铺,讀取數(shù)據(jù)的方法就能解決問(wèn)題尝哆。這個(gè)方法完全正確。如果你對(duì)文件的基本操作還有問(wèn)題甜攀,請(qǐng)參考我之前的文章21天C語(yǔ)言代碼訓(xùn)練營(yíng)(第十五天)秋泄。

這里分享一段Aha_斌提交作業(yè)中的代碼,請(qǐng)參考规阀。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//用宏去存儲(chǔ)文件路徑
#define FILE_PATH "input.txt" 

void main()
{
    FILE *fp;
    char str[50];//存儲(chǔ)從文件讀取的字符串
    int strNum;
    char ch;
    if ((fp = fopen(FILE_PATH, "r")) == NULL)
    {
        printf("System can`t find this file!\n");
        exit(0);
    }
    fscanf(fp, "%d", &strNum);//讀取要比較表達(dá)式的個(gè)數(shù)
    fscanf(fp, "%c", &ch);//讀取第一行的換行符
    while (strNum--)//當(dāng)表達(dá)式?jīng)]有比較完
    {
        fgets(str, 50, fp);//讀取一個(gè)表達(dá)式
        
        // 這里加入判斷str是否符合要求的代碼
    }
    system("PAUSE");
}

3.2 文件流重定向方法

這里我要介紹另外一種方法恒序,這是ACM比賽中常用的一種數(shù)據(jù)讀取方法。它的特點(diǎn)是把打開(kāi)的文件重定向到標(biāo)準(zhǔn)輸入流中姥敛,這樣我們就可以像讀取鍵盤(pán)輸入數(shù)據(jù)一樣讀入數(shù)據(jù)奸焙。

先看一下這段代碼:

int main()
{
    int n, i;
    int ret;
    char str[MAX_SIZE];
    FILE* fp;
    freopen_s(&fp, "input.txt", "r", stdin);
    if (fp == NULL)
    {
        ret = 0;
    }

    scanf("%d", &n);
    printf("%d\n", n);

    for (i = 0; i < n; i++)
    {
        scanf("%s", str);
        printf("%s\n", str);
    }
}

這段代碼的功能是把input.txt文件中的內(nèi)容讀入程序,之后打印在屏幕上彤敛。很容易發(fā)現(xiàn),我們?cè)诖蜷_(kāi)文件并沒(méi)有使用大家熟悉的fopen()函數(shù)數(shù)了赌,而是用了這句話(huà):

freopen_s(&fp, "input.txt", "r+", stdin);

這句話(huà)的作用是把input.txt文件用只讀方式打開(kāi)墨榄,之后重定向到stdin表示的標(biāo)準(zhǔn)輸入流中。當(dāng)fp為空時(shí)勿她,表示此文件不存在袄秩。在這之后,我們可以用scanf()函數(shù)像讀取鍵盤(pán)輸入那樣讀取文件內(nèi)容了。是不是很神奇之剧。

如不需要得到文件指針郭卫,我們還可以寫(xiě)成這樣:

freopen("input.txt", "r+", stdin);

這個(gè)形式是標(biāo)準(zhǔn)庫(kù)函數(shù)的寫(xiě)法。

我們來(lái)看看執(zhí)行結(jié)果:

4. 判斷括號(hào)匹配

4.1 常見(jiàn)方法

完成了作業(yè)的同學(xué)都采用了一種最容易想到的方法背稼,那就是分別統(tǒng)計(jì)[]()四個(gè)符號(hào)的個(gè)數(shù)贰军,之后比較個(gè)數(shù)是否相同,如果相同就說(shuō)明匹配蟹肘,如果不相同就說(shuō)明不匹配词疼。

但是這種方法有很多bug,比如))((這種問(wèn)題帘腹。還有我新加入的幾個(gè)公式都會(huì)判斷錯(cuò)誤贰盗。不過(guò)也不是完全不可能,只不過(guò)需要另外添加一些約束條件阳欲,多寫(xiě)一些代碼舵盈。有興趣的同學(xué)可以參考完成作業(yè)的代碼。

4.2 逐個(gè)匹配法

我們這里介紹一種大家沒(méi)有想到的算法球化。這個(gè)算法的思想是這樣的书释,從前到后遍歷表達(dá)式,忽略字母和運(yùn)算符赊窥,只關(guān)注括號(hào)爆惧。一旦遇到匹配成功的括號(hào)就整對(duì)刪掉,如果最后剩下半個(gè)括號(hào)(無(wú)論是左半邊還是右半邊)锨能,說(shuō)明不匹配扯再,否則就說(shuō)明是匹配的。

如圖址遇,我們利用一個(gè)數(shù)組來(lái)完成它熄阻,具體分為三個(gè)動(dòng)作:

  • 遇到左括號(hào)寫(xiě)入數(shù)組
  • 遇到右括號(hào)拿它和數(shù)組中最后一個(gè)括號(hào)做匹配
  • 匹配成功兩個(gè)括號(hào)都刪除,繼續(xù)后面的動(dòng)作
  • 匹配失敗直接跳出倔约,返回失敗

我們看看代碼如何實(shí)現(xiàn):

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

#define MAX_SIZE 100

char g_arr[MAX_SIZE];
int g_index;

int CheckChar(char ch)
{
    if (ch == '(')
    {
        g_arr[g_index++] = ch;
    }
    else if (ch == '[')
    {
        g_arr[g_index++] = ch;
    }
    else if (ch == ')')
    {
        if (g_arr[g_index - 1] == '(')
        {
            g_index--;
        }
        else
        {
            return 0;
        }
    }
    else if (ch == ']')
    {
        if (g_arr[g_index - 1] == '[')
        {
            g_index--;
        }
        else
        {
            return 0;
        }
    }
    else
    {
        // Do nothing
    }

    return 1;
}

int Check(char* pStr)
{
    int i;

    g_index = 0;
    
    for (i = 0; pStr[i] != '\0'; i++)
    {
        if (CheckChar(pStr[i]) == 0)
        {
            return 0;
        }
    }

    if (g_index == 0)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

5. 匹配算法分析

我們來(lái)仔細(xì)分析一下這段代碼秃殉,它一共做了三件事。

5.1 定義數(shù)據(jù)結(jié)構(gòu)

#define MAX_SIZE 100

int g_arr[MAX_SIZE];
int g_index;

數(shù)據(jù)結(jié)構(gòu)非常簡(jiǎn)單浸剩,就是一個(gè)大小為MAX_SIZE的一維字符數(shù)組钾军。我們用g_index這個(gè)變量保存這個(gè)數(shù)組的當(dāng)前下標(biāo),也就是按順序存入內(nèi)容時(shí)最左邊的那個(gè)空位绢要。

這個(gè)數(shù)組的使用方法如圖所示吏恭,每插入一個(gè)字符g_index加1,標(biāo)記為一個(gè)新的空白位置重罪。刪除時(shí)動(dòng)作相反樱哼。

注意哀九,這里使用全局變量只是為了方便大家理解,真正的項(xiàng)目中并不鼓勵(lì)用全局變量的方式設(shè)計(jì)代碼搅幅。

5.2 判斷字符

int CheckChar(char ch)

這個(gè)函數(shù)主要負(fù)責(zé)使用g_arr進(jìn)行匹配動(dòng)作阅束。把表達(dá)式中的每個(gè)字符傳遞給參數(shù)ch,它會(huì)負(fù)責(zé)判斷這個(gè)符號(hào)是否符合要求茄唐。如果拿到的符號(hào)不是括號(hào)息裸,直接忽略掉。如果是左括號(hào)就存入數(shù)組琢融,如果是右括號(hào)就和數(shù)組中最后一個(gè)括號(hào)(g_arr[g_index - 1])做匹配界牡。

這里重點(diǎn)要說(shuō)的是匹配成功后,需要把數(shù)組中最后一個(gè)字符刪掉漾抬。只用了一句話(huà):

g_index--;

這句話(huà)用到了一個(gè)重要的思想宿亡,刪除最后一個(gè)元素只需要更新當(dāng)前下標(biāo),并不用真的把那一位改成0纳令。我們?cè)诰S護(hù)g_arr這個(gè)數(shù)組時(shí)挽荠,其實(shí)看的是g_index的值,我們定義了它永遠(yuǎn)標(biāo)記的是第一個(gè)空著的可用位置平绩。如果g_index標(biāo)記了一個(gè)有內(nèi)容的位置圈匆,那么下一次寫(xiě)入時(shí)會(huì)通過(guò)g_arr[g_index++] = '(';這樣的語(yǔ)句把這塊內(nèi)容覆蓋掉。所以捏雌,更新g_index的方法和刪除這塊內(nèi)容的效果是完全一樣的跃赚。

5.3 處理字符串

int Check(char* pStr)

這個(gè)函數(shù)是為了方便我們循環(huán)處理每個(gè)表達(dá)式字符串。只要把表達(dá)式字符串傳遞給參數(shù)pStr性湿,它會(huì)通過(guò)調(diào)用CheckChar()函數(shù)逐個(gè)分析每個(gè)字符纬傲,直到判斷出結(jié)果。

函數(shù)最前面肤频,有這樣一行代碼:

g_index = 0;

按照前面講過(guò)的理論叹括,它其實(shí)完成的是g_arr數(shù)組清空的動(dòng)作。是不是很方便宵荒。

6. 功能整合

最后汁雷,我們需要把從文件中讀入的數(shù)據(jù)通過(guò)前面的功能進(jìn)行處理。代碼如下:

int main()
{
    int n, i;
    int ret;
    char str[50];
    FILE* fp;
    freopen_s(&fp, "input.txt", "r+", stdin);
    if (fp == NULL)
    {
        ret = 0;
    }

    scanf("%d", &n);

    for (i = 0; i < n; i++)
    {
        scanf("%s", str);
        ret = Check(str);
        printf("%d\n", ret);
    }
}

執(zhí)行結(jié)果如下:

7. 課后作業(yè)

編程統(tǒng)計(jì)出input.txt文件保存的文章中报咳,每個(gè)單詞出現(xiàn)的次數(shù)侠讯。文章內(nèi)容如下:

In this chapter we will be looking at files and directories and how to manipulate them. We will learn how to create files, open them, read, write and close them. We'll also learn how programs can manipulate directories, to create, scan and delete them, for example. After the last chapter's diversion into shells, we now start programming in C.
Before proceeding to the way UNIX handles file I/O, we'll review the concepts associated with files, directories and devices. To manipulate files and directories, we need to make system calls (the UNIX parallel of the Windows API), but there also exists a whole range of library functions, the standard I/O library (stdio), to make file handling more efficient.

這段文字來(lái)自網(wǎng)絡(luò)。為了統(tǒng)計(jì)更有意義少孝,加入兩個(gè)條件:

  • 統(tǒng)計(jì)過(guò)程中不考慮空格和標(biāo)點(diǎn)符號(hào)
  • 不區(qū)分大小寫(xiě)(可以把所有字母轉(zhuǎn)成小寫(xiě)后參與統(tǒng)計(jì))

我是天花板继低,讓我們一起在軟件開(kāi)發(fā)中自我迭代。
如有任何問(wèn)題稍走,歡迎與我聯(lián)系袁翁。


上一篇:天花板編程手把手計(jì)劃-第1期-第5天

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市婿脸,隨后出現(xiàn)的幾起案子粱胜,更是在濱河造成了極大的恐慌,老刑警劉巖狐树,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件焙压,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡抑钟,警方通過(guò)查閱死者的電腦和手機(jī)涯曲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)在塔,“玉大人幻件,你說(shuō)我怎么就攤上這事』桌#” “怎么了绰沥?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)贺待。 經(jīng)常有香客問(wèn)我徽曲,道長(zhǎng),這世上最難降的妖魔是什么麸塞? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任秃臣,我火速辦了婚禮,結(jié)果婚禮上哪工,老公的妹妹穿的比我還像新娘奥此。我一直安慰自己,他們只是感情好正勒,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布得院。 她就那樣靜靜地躺著,像睡著了一般章贞。 火紅的嫁衣襯著肌膚如雪祥绞。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,165評(píng)論 1 299
  • 那天鸭限,我揣著相機(jī)與錄音蜕径,去河邊找鬼。 笑死败京,一個(gè)胖子當(dāng)著我的面吹牛兜喻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播赡麦,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼朴皆,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼帕识!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起遂铡,我...
    開(kāi)封第一講書(shū)人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤肮疗,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后扒接,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體伪货,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年钾怔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了碱呼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡宗侦,死狀恐怖愚臀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情凝垛,我是刑警寧澤懊悯,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站梦皮,受9級(jí)特大地震影響炭分,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜剑肯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一捧毛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧让网,春花似錦呀忧、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至因篇,卻和暖如春泞辐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背竞滓。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工咐吼, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人商佑。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓锯茄,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親茶没。 傳聞我的和親對(duì)象是個(gè)殘疾皇子肌幽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 題目 原問(wèn)題鏈接編程統(tǒng)計(jì)出input.txt文件保存的文章中晚碾,每個(gè)單詞出現(xiàn)的次數(shù)。文章內(nèi)容如下: In this ...
    Hans941閱讀 389評(píng)論 0 2
  • 上一篇中牍颈,我們布置了一道關(guān)于二維數(shù)組使用的練習(xí)題迄薄。大家做的都不錯(cuò)琅关,我們今天來(lái)仔細(xì)分析一下這道題目煮岁,看看有沒(méi)有你沒(méi)有...
    天花板閱讀 1,948評(píng)論 3 26
  • 今天我們來(lái)講解一下上一篇的課后習(xí)題。 1. 題目 編程實(shí)現(xiàn)把1~9九個(gè)數(shù)字填入九宮格中涣易,滿(mǎn)足每行画机、每列和對(duì)角線(xiàn)上的...
    天花板閱讀 1,584評(píng)論 5 16
  • 實(shí)現(xiàn)隔行篩選的兩種方式: 1.0/1 復(fù)制單元格 2.使用mod 與 row 結(jié)合 返回兩數(shù)相除的余數(shù) MOD(n...
    雪呢閱讀 248評(píng)論 0 1
  • 01 小Y自小是個(gè)乖巧, 懂事的孩子新症, 鄰居步氏, 叔叔阿姨, 老師都喜歡徒爹。 在幼兒園和小學(xué)時(shí)候荚醒, 經(jīng)常爸爸媽媽帶著一...
    涼州辭閱讀 220評(píng)論 0 1