【问题标题】:using 2-d arrays within functions and word count in C在函数中使用二维数组并在 C 中使用字数
【发布时间】:2018-02-27 05:23:08
【问题描述】:

我是 C 新手,业余时间一直在学习。我正在努力掌握函数,因此对这些函数的任何帮助都会很棒,而且还能让我的字数发挥作用。

我的程序是计算文件中所有单词的出现次数,并产生一个单词计数。我已经尽力而为,并且在主函数中显示所有事件时让它工作,但是当使用我自己的函数时它们不起作用。

由于某种我似乎无法弄清楚的原因,我的字数也不起作用。

任何帮助将不胜感激!

最诚挚的问候, 约翰

#include <stdio.h>
#include <string.h>
#include <stdlib.h>


void print_occur_word(int initialarray[1000][10], int finalarray[1000][10], words);

void count_words(initialarray[1000][10])

int main(int argc, char *argv[])
{
    FILE *file = fopen("file.txt", "rb");//opens sentences file
    fseek(file, 0, SEEK_END);
    long fsize = ftell(file);
    fseek(file, 0, SEEK_SET);

    char *str = malloc(fsize + 1);//uses malloc to  find size
    fread(str, fsize, 1, file);
    fclose(file);//closes

    str[fsize] = 0;
    int count = 0, c = 0, i, j = 0, appearance, space = 0, temp, temp1;
    char initialarray[1000][10], finalarray[1000][10];
    char *ptr;

    for (i = 0;i<strlen(str);i++)
    {
        if ((str[i] == ' ')||(str[i] == ',')||(str[i] == '.'))
        {
            space++;
        }
    }


    for (i = 0, j = 0, appearance = 0;j < strlen(str);j++)
    {
        if ((str[j] == ' ')||(str[j] == 44)||(str[j] == 46))
        {
            initialarray[i][appearance] = '\0';
            i++;
            appearance = 0;
        }
        else
            initialarray[i][appearance++] = str[j];
    }

    appearance = 0;
    for (i = 0;i <= space;i++)
    {
        for (j = 0;j <= space;j++)
        {
            if (i == j)
            {
                strcpy(finalarray[appearance], initialarray[i]);
                appearance++;
                count++;
                break;
            }
            else
            {
                if (strcasecmp(finalarray[j], initialarray[i]) != 0)
                    continue;
                else
                    break;
            }
        }
    }

    count_words(initialarray);
    print_occur_word(initialarray, finalarray, words);

    return 0;

}

int count_words(int initialarray[1000][10])
{
    int i,words=0;

    if(strlen(initialarray)==1)//if there are no words
        printf("Number of words in array:=%d",words);

    else
    {

        for(i=0;initialarray[i]!='\0';i++)//loop for finding words
        {
            //checking for blank space,new line and tab
            if((initialarray[i]==32)||initialarray[i]=='\t'||initialarray[i]=='\n')
                words++;//incrementing count of words
        }

        if(initialarray[i]=='\0')
            words++;
        return words;

    }
}

void print_occur_word(int initialarray[1000][10], int finalarray[1000][10], words)
{
    Printf("Number of words are: %d\n", words);
    int i, count, space, j, c;
    for (i = 0;i < count;i++)
    {
        for (j = 0;j <= space;j++)
        {
            if (strcasecmp(finalarray[i], initialarray[j]) == 0)
                c++;
        }
        printf("%s : %d \n", finalarray[i], c);
        c = 0;
    }
}

【问题讨论】:

  • 使用','代替44和'.'代替46,使代码更具可读性。
  • 你真正需要学习的是stringsarraysPOINTERS。因为很明显你不知道。这是 c 语言的微妙部分之一,它使 c 有点难以学习。一旦你学会了它,它就会变得非常简单。同时,启用编译器警告,因为strlen(initialarray) 无法正常工作。此外,您if(strlen(initialarray)==1) 的逻辑也很难理解。
  • 我不假装冒犯,只是诚实和直接。
  • @IharobAlAsimi 根本没有采取那种方式。我将在下周回去重新学习这些内容。感谢您的指导。
  • @Pablo 可以。谢谢。

标签: c function word-count


【解决方案1】:

这不是一个容易解决的问题,因为你要拆分内容, 存储单词,计算单词等。涉及的步骤很多。

首先,我注意到了一些事情:

if ((str[j] == ' ')||(str[j] == 44)||(str[j] == 46))

使用 ASCII 值是不是不正确的,但它使阅读代码 更难,因为我们审查您的代码,以及您以后。使用字符 而是:

if ((str[j] == ' ')||(str[j] == ',')||(str[j] == '.'))

这更容易阅读。你也没有考虑到什么时候 示例多个分隔符一个接一个出现: "these are my thoughts.... I'd like to say"。你数到很多“空格” 并将许多空字符串添加到initialarray

当你这样声明时:

char initialarray[1000][10];

您将自己限制为最多 1000 个字,最大长度为 9。那就是 好的,但是您必须检查您的书写是否超出范围。你是 不做任何边界检查,如果超过 1000 字,就会溢出 缓冲区,如果单词超过 9 个字符,则溢出 缓冲区。

我会这样写:首先定义一个结构,其中包含单词和 出场次数。为每个单词创建一个结构数组,当你 添加一个新词,您必须检查该词是否已经存在。如果是,那么 计数器加一,否则添加新单词并将其计数设置为 1。例如:

typedef struct word_count {
    char *word;
    size_t count;
} word_count;


int add_word(word_count **wc, size_t *len, const char *word)
{
    if(wc == NULL || word == NULL || len == NULL)
        return 0;

    // empty list
    if(*wc == NULL)
        *len = 0;

    // search for word
    for(size_t i = 0; i < *len; ++i)
    {
        if(strcasecmp((*wc)[i].word, word) == 0)
        {
            (*wc)[i].count++;
            return 1;
        }
    }

    word_count *nwc = realloc(*wc, (*len + 1) * sizeof *nwc);
    if(nwc == NULL)
        return 0;

    // creating copy of word
    nwc[*len].word = strdup(word);
    if(nwc->word == NULL)
        return 0; // do not update *len

    nwc[*len].count = 1;
    *wc = nwc;
    (*len)++;
    return 1;
}

void free_words(word_count *wc, size_t len)
{
    if(wc == NULL)
        return;

    for(size_t i = 0; i < len; ++i)
        free(wc[i].word);
    free(wc);
}

使用add_word,我同时存储单词并计算它们。首先我去 通过数组并检查单词是否已经存储在数组中。在那里面 情况下我只增加计数。如果这是一个新词,那么我重新分配 再记忆一个单词并添加单词并将计数设置为 1。

现在你可以这样做了:

void foo(void)
{
    size_t len = 0;
    word_count *wc = NULL;

    add_word(&wc, &len, "word1");
    add_word(&wc, &len, "word2");
    add_word(&wc, &len, "word3");
    add_word(&wc, &len, "word2");
    add_word(&wc, &len, "wORD1");

    if(wc)
    {
        for(size_t i = 0; i < len; ++i)
            printf("word: '%s', count: %zu\n", wc[i].word, wc[i].count);
    }

    free_words(wc, len);
}

你会得到:

word: 'word1', count: 2
word: 'word2', count: 2
word: 'word3', count: 1

然后统计单词的函数使用strtok来获取单词。 strtok 修改源字符串,所以为了保留原始内容,我做 复制并在上面使用strtok

word_count *get_word_counts(const char *file, size_t *len)
{
    if(file == NULL || len == NULL)
        return NULL;

    word_count *wc = NULL;
    *len = 0;

    // I do a copy because strtok modifies the source
    // preserving the original content
    char *copy = strdup(file);
    if(copy == NULL)
    {
        fprintf(stderr, "Not enough memory\n");
        return NULL;
    }

    const char *delim = " \t,.\r\n!"; // \n to consume newlines as well

    char *token = strtok(copy, delim);

    do {
        // if empty word
        if(token[0] == 0)
            continue;

        if(add_word(&wc, len, token) == 0)
        {
            fprintf(stderr, "failed to add word '%s'\n", token);
            free(copy);
            return wc; // returning all words so far
        }

    } while((token = strtok(NULL, delim)));

    free(copy);

    return wc;
}

我将这些函数放在一起并创建了一个文本文件,其中包含您的问题 内容。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

typedef struct word_count {
    char *word;
    size_t count;
} word_count;

int add_word(word_count **wc, size_t *len, const char *word)
{
    if(wc == NULL || word == NULL || len == NULL)
        return 0;

    // empty list, create one
    if(*wc == NULL)
        *len = 0;

    // search for word
    for(size_t i = 0; i < *len; ++i)
    {
        if(strcasecmp((*wc)[i].word, word) == 0)
        {
            (*wc)[i].count++;
            return 1;
        }
    }

    word_count *nwc = realloc(*wc, (*len + 1) * sizeof *nwc);
    if(nwc == NULL)
        return 0;

    nwc[*len].word = strdup(word);
    if(nwc->word == NULL)
        return 0; // do not update *len

    nwc[*len].count = 1;
    *wc = nwc;
    (*len)++;
    return 1;
}

void free_words(word_count *wc, size_t len)
{
    if(wc == NULL)
        return;

    for(size_t i = 0; i < len; ++i)
        free(wc[i].word);
    free(wc);
}


long get_file_size(const char *filename)
{
    if(filename == NULL)
        return -1;

    FILE *file = fopen(filename, "rb");
    if(file == NULL)
    {
        fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
        return -1;
    }

    if(fseek(file, 0, SEEK_END) == -1)
    {
        fprintf(stderr, "Could not seek to the end: %s\n", strerror(errno));
        fclose(file);
        return -1;
    }

    long fsize = ftell(file);

    fclose(file);

    return fsize;
}

char *get_file(const char *filename, long *filesize)
{
    if(filename == NULL)
        return NULL;

    long fs = get_file_size(filename);
    if(fs == -1)
    {
        fprintf(stderr, "Could not calculate file size\n");
        return NULL;
    }

    if(filesize)
        *filesize = fs;

    // I use calloc so that the buffer is \0-terminated
    char *res = calloc(1, fs + 1);
    if(res == NULL)
        return NULL;


    FILE *fp = fopen(filename, "rb");
    if(fp == NULL)
    {
        fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
        free(res);
        fclose(fp);
        return NULL;
    }

    if(fread(res, 1, fs, fp) != fs)
    {
        fprintf(stderr, "Could not get the whole file\n");
        free(res);
        fclose(fp);
        return NULL;
    }

    fclose(fp);

    return res;
}

word_count *get_word_counts(const char *file, size_t *len)
{
    if(file == NULL || len == NULL)
        return NULL;

    word_count *wc = NULL;
    *len = 0;

    // I do a copy because strtok modifies the source
    // preserving the original content
    char *copy = strdup(file);
    if(copy == NULL)
    {
        fprintf(stderr, "Not enough memory\n");
        return NULL;
    }

    const char *delim = " \t,.\r\n!"; // \n to consume newlines as well

    char *token = strtok(copy, delim);

    do {
        // if empty word
        if(token[0] == 0)
            continue;

        if(add_word(&wc, len, token) == 0)
        {
            fprintf(stderr, "failed to add word '%s'\n", token);
            free(copy);
            return wc; // returning all words so far
        }

    } while((token = strtok(NULL, delim)));

    free(copy);

    return wc;
}


int cmp_count(const void *s1, const void *s2)
{
    word_count *w1 = (word_count*) s1, *w2 = (word_count*) s2;
    return strcasecmp(w1->word, w2->word);
}

int main(int argc, char **argv)
{

    int ret = 0;
    if(argc != 2)
    {
        fprintf(stderr, "usage: %s file\n", argv[0]);
        return 1;
    }

    long fs = 0;
    char *file = get_file(argv[1], &fs);
    if(file == NULL)
        return 1;

    size_t len = 0;
    word_count *wc = get_word_counts(file, &len);

    if(wc == NULL)
    {
        fprintf(stderr, "failed to count words\n");
        ret = 1;
        len = 0;
    }

    // sorting words
    if(wc)
        qsort(wc, len, sizeof *wc, cmp_count);

    for(size_t i = 0; i < len; ++i)
        printf("word: '%s', count: %zu\n", wc[i].word, wc[i].count);

    free_words(wc, len);
    free(file);
    return ret;
}

输出是:

word: 'a', count: 1
word: 'all', count: 2
word: 'also', count: 2
word: 'am', count: 2
word: 'and', count: 3
word: 'any', count: 2
word: 'appearances', count: 1
word: 'appreciated', count: 1
word: 'at', count: 1
word: 'be', count: 2
word: 'been', count: 1
word: 'best', count: 1
word: 'but', count: 2
word: 'C', count: 1
word: 'can't', count: 1
word: 'count', count: 4
...

请注意,我使用strdup 创建单词和文件内容的副本。如果你的系统 没有strdup,你可以用这个:

char *strdup(const char *text)
{
    if(text == NULL)
        return;

    char *copy = malloc(strlen(text) + 1);
    if(copy == NULL)
        return NULL;

    return strcpy(copy, text);
}

编辑

OP 在评论区询问

我将如何通过首先出现最多次数来对文件进行排序?

您需要做的就是更改函数cmp_count 或创建一个新函数 不同的比较。 qsort 传递一个指针 到需要比较的值,如果值必须返回 0 相等,如果左值小于则小于 0,如果左值大于 0 左值较大。在降序排序的情况下,您必须交换符号, 这意味着如果左值更大,则返回小于 0 的值,并且 如果左值较小,则大于 0。

所以,如果你想按出现次数排序,你必须检查w1-&gt;count w2-&gt;count

int cmp_count_by_count_desc(const void *s1, const void *s2)
{
    word_count *w1 = (word_count*) s1, *w2 = (word_count*) s2;
    return w2->count - w1->count;
}

那么您必须将main 中的qsort 行更改为:

if(wc)
    qsort(wc, len, sizeof *wc, cmp_count_by_count_desc);

使用新排序运行相同的测试输入,我得到

word: 'to', count: 6
word: 'my', count: 6
word: 'I', count: 4
word: 'count', count: 4
word: 'the', count: 4
word: 'and', count: 3
word: 'have', count: 3
word: 'word', count: 3
word: 'work', count: 3
word: 'am', count: 2
word: 'in', count: 2
word: 'Functions', count: 2
word: 'any', count: 2
word: 'help', count: 2
word: 'would', count: 2
word: 'be', count: 2
word: 'but', count: 2
word: 'also', count: 2
word: 'of', count: 2
word: 'all', count: 2
word: 'it', count: 2
word: 'when', count: 2
word: 'new', count: 1
word: 'C', count: 1
word: 'been', count: 1
...

【讨论】:

  • 所以通过使用结构我们能够动态调整它的大小,这样我们就不会限制自己的内存分配?此外,在我发布这个问题后,我对 Strtok 进行了一些挖掘,发现它是使用我拥有的技能集计算出现次数的最有益的方式(我不太了解)。
  • @JohnSmith 是的,感谢reallocstrdup,您的文件可以包含任意长度的单词(当然,因为有可用的内存)
  • 您好,我将如何通过首先出现最多次数来对文件进行排序?谢谢,约翰
  • @JohnSmith 我已经通过解决您的问题更新了我的答案。
  • @JohnSmith 我看到你删除了下一个问题。 add_word 函数不关心字符串是在何处、何时以及如何获取的。所以你只需要改变main 函数。您可以不使用char *file = get_file(argv[1], &amp;fs);,而是:char line[1024]; fgets(line, sizeof line, stdin); 并调用word_count *wc = get_word_counts(file, &amp;len);。其余代码保持不变。
猜你喜欢
  • 2015-03-19
  • 2013-11-08
  • 2021-01-28
  • 2018-09-03
  • 2022-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多