天花板编程手把手计划-第1期-第6天-打卡

题目

原问题链接
编程统计出input.txt文件保存的文章中,每个单词出现的次数。文章内容如下:

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.

这段文字来自网络。为了统计更有意义,加入两个条件:

统计过程中不考虑空格和标点符号
不区分大小写(可以把所有字母转成小写后参与统计)

解题思路

做一个词频统计工具
观察文本发现,两个单词之间都有空格隔开。这就比较好处理了,因为我们可以很轻松的实现一次读取一个单词。

如果实际要处理的文本有标点之后没空格的情况,如

I love you.My program.

那我们还要加一个deliver函数把you和My分开

如何存储出现过的单词
1.二维数组搭配一个一维数组实现
2.利用结构体做一个链表实现
我选择了第二种,因为我觉得结构体是通往抽象编程的路,而且使用结构体可读性更好。

具体思路
1.一次从文本读取一个字符串word
2.把字符串word的大写字母转化为小写
3.把字符串word里的 ,.() 四种标点消除(想做成一个好用的词频统计工具的话,根据实际需要增添要消除的标点符号)
4.检查word是否出现过
没有出现过: 把他存入结构体
出现过:找到存储这个word的结构体,并把里面的计数变量加一
5.打印所有存储了单词的结构体

源码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FILE_PATH "..\\要统计的文本.txt" //宏存储文本相对路径
#define WORD_LENGTH 60
#define LENGTH sizeof(WORD)

struct WORD
{
    char word[WORD_LENGTH];     //存放单词
    int wordCount;        //本单词出现次数
    struct WORD *pNext;            //next指针
};
struct WORD pHeader;      //链表的头结点

WORD *countWord(char *word, WORD *pLastWord);
void changeToLower(char *word);
void checkPunct(char *word);
int checkCountWord(char *word);
void addcount(char * word);
WORD * addNewWord(char *word, WORD *pLastWord);
void printResult();
void freeAll();

void main()
{
    WORD *pLastWord = &pHeader;//指向最后一个结点的指针
    char tempWord[WORD_LENGTH];//储存本次读取的字符串
    pHeader.pNext = NULL;//把头节点的指向初始化为空
    FILE *fp;
    if ((fp = fopen(FILE_PATH, "r")) == NULL)//如果打开文本为空
    {
        printf("System can`t find this file!\n");
        exit(0);
    }
    while ((fscanf(fp, "%s", tempWord)) != EOF)//如果文件还没读取完
    {
        pLastWord = countWord(tempWord, pLastWord);//统计词频
    }
    fclose(fp);//关闭文件
    printResult();//打印统计结果
    freeAll();//释放所有动态申请的内存
    system("PAUSE");
}

WORD *countWord(char *word,WORD *pLastWord)
{
    
    changeToLower(word);//把Word全部转换为小写字母
    checkPunct(word);//去除word中的 (),. 如果文本含有其他特殊标点,这里需要再增加一点代码
    if (checkCountWord(word))//如果本单词在前文出现过
    {
        addcount(word);//把这个单词的计数加一
    }
    else
    {
        pLastWord = addNewWord(word, pLastWord);//建立一个新的单词结点
    }
    return pLastWord;
}

void changeToLower(char *word)//把字符串全部转化为小写字母
{
    int i;
    for (i = 0; word[i] != '\0'; i++)
    {
        if (word[i] >= 65 && word[i] <= 90)
        {
            word[i] += 32;
        }
    }
}

void checkPunct(char *word)//去除标点符号
{
    int i, k;
    for (i = 0; word[i] != '\0'; i++)//遍历字符串
    {
        if (word[i] == '(' || word[i] == ')' || word[i] == ',' || word[i] == '.')//如果含有标点
        {
            for (k = i; word[k] != '\0'; k++)
            {
                word[k] = word[k + 1];
            }
            i--;//防止"sdio)."这种连续多个特殊字符出现的情况,再次检查当前index下的字符
        }
    }
}

int checkCountWord(char *word)//不在已存储单词中返回0.  在就返回1
{
    WORD *p;
    p = &pHeader;
    if (pHeader.pNext == NULL)//如果检查的是第一个单词
    {
        return 0;
    }
    do
    {
        p = p->pNext;
        if (strcmp(word, p->word) == 0)
        {
            return 1;
        }
    } while (p->pNext != NULL);
    return 0;
}

void addcount(char * word)//把出现过的单词计数加一
{
    WORD *p;
    p = &pHeader;
    do
    {
        p = p->pNext;
        if (strcmp(word, p->word) == 0)//遍历到重复的结点,并把计数 +1 
        {
            p->wordCount++;
            return;
        }
    } while (p->pNext != NULL);
}

WORD * addNewWord(char *word, WORD *pLastWord)//加一个新单词结点
{
    WORD *pTemp;
    if (pHeader.pNext == NULL)//如果添加的是第一个结点
    {
        pHeader.pNext = (WORD *)malloc(LENGTH);
        strcpy(pHeader.pNext->word, word);
        pHeader.pNext->wordCount = 1;
        pHeader.pNext->pNext = NULL;
        pLastWord = pHeader.pNext;
    }
    else
    {
        pTemp = pLastWord;
        pLastWord = (WORD *)malloc(LENGTH);
        strcpy(pLastWord->word, word);
        pLastWord->wordCount = 1;
        pLastWord->pNext = NULL;
        pTemp->pNext = pLastWord;
    }
    return  pLastWord;
}

void printResult()//打印存储的所有单词结点
{
    WORD *p;
    int sign = 0;
    p = &pHeader;
    do
    {
        p = p->pNext;
        printf("%-12s%d\t\t", p->word, p->wordCount);
        sign++;
        if (sign % 3 == 0)
        {
            printf("\n");
        }
    } while (p->pNext != NULL);
}

void freeAll()
{
    WORD *p ,*piror;
    p = pHeader.pNext;//头节点不是动态申请的不能释放
    do
    {
        piror = p;
        p = p->pNext;
        free(piror);
    } while (p->pNext != NULL);
    free(p);
}

执行结果

统计结果

后来改成了双向链表并在打印前加了排序的函数,下载了一本英文版的 <百年孤独> 进行了词频统计
很好用(乱码是因为下载的文本文件本身就有乱码...)
就是运行效率有些低

统计<百年孤独>运行时间记录(800K的文本文件)

  • 打印结果占用2秒
  • 排序代码占用1秒
  • 其他代码占用3秒
  • 总计运行时间6秒
百年孤独的部分统计结果

总结

去年考英语六级时,我下载了一本英文电子版的<The Great Gatsby>,想统计出它的高频单词并背诵。我用了一个在线词频统计工具来统计,因为各种特殊字符的存在结果特别不理想。
现在学会了写自己的程序,我可以根据自己的实际需求来改写出合适的程序。It's really cool!
而且这个链表思维和我写的贪吃蛇实现蛇身体的思维很相似,我可以优化我的贪吃蛇程序了!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容

  • 从今天起,我们要开始学习用工程化的思想去解决问题。之前,我们总是把所有的代码写在一个源文件中,这样看起来比较方便。...
    天花板阅读 2,118评论 1 30
  • 这一期的内容已经过半,部分同学开始觉得吃力。如果这时候放弃,那前边的努力就白费了。今天我们来看看上一篇中的课后题。...
    天花板阅读 1,585评论 0 13
  • 初识瑜伽源于朋友的瘦身成功,从朋友处听说了瑜伽的好处瘦身、减压、排毒、完美体形、预防慢性病、消除紧张和疲劳、保持青...
    爱思嘉阅读 286评论 2 5
  • 一'我学到了什么 这一学期我们学习了第一单元 小数除法,第二单元 轴对称和平移,第三单元 倍数和因数。下面我就把每...
    梁志敏LZM阅读 1,482评论 0 1