【问题标题】:Word count application - C字数统计应用程序 - C
【发布时间】:2014-02-23 19:06:25
【问题描述】:

我目前正在尝试编写一个应用程序来计算 ASCII 文件中单词的出现次数(去除标点符号并忽略空格)。应用程序应将单词和单词计数存储在数据结构中,最终将按降序排序,然后打印到 CSV 文件。

我已开始使用此程序,但在尝试保存新单词时遇到了分段错误。这是我的代码(我知道这不是一个完美的实现,我确实计划改进它):

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

#define TRUE 1
#define FALSE 0

/* This program is designed to take an ASCII input file, count the occurrences of words in it
 * and write an output file displaying the data. I intend for it to convert uppercase to 
 * lowercase, so as not to generate duplicate words in the data structure. It should also 
 * ignore whitespace and punctuation.
*/

void getWords(void);
void printFile(void);
void save(char *input);

struct word {
    char *str;
    int wc;
};

struct word *warray = NULL;

FILE *infile;
FILE *outfile;

void getWords(void)
{

    rewind(infile);
    char cw[100]; // Current word storage
    int i = 0, j = 0, c;

    while((c = fgetc(infile)) != EOF)
    {
        if(isalpha(c))
        {
            if(isupper(c))
            {
                cw[i] = tolower(c);
                ++i;
            }
            else
            {
                cw[i] = c;
                ++i;
            }
        }
        else
        {
            if(c == '\n' || c == '\t' || c == ' ')
            {
                cw[i] = '\0';
                i = 0;
                save(cw);

                for(j = 0; j < cw[99]; j++)
                {
                    printf("%c", cw[j]);
                }
            }
        }

    }

}

void printFile(void)
{

    int i, c;

    printf("Printing the file to be counted in lowercase...\n");
    for(i = 0; (c = fgetc(infile)) != EOF; i++)
    {
        if(ispunct(c) || isdigit(c))
        {
            ++i;
        }
        else
        {
            putchar(tolower(c));
        }

    }
}

void save(char *input)
{

    int exists = FALSE, i = 0;
    int elements = sizeof(warray)/sizeof(struct word);

    if(!warray)
    {
        warray = malloc(sizeof(struct word));
        printf("Made array.\n");
    }
    else
    {
        printf("New.\n");
        warray = realloc(warray, (elements++)*sizeof(struct word));
    }

    while(i < elements)
    {
        printf("in while loop\n");
        if(strcmp(input, warray[i].str) == 0)
        {
            warray[i].wc++;
        }
        else
        {
            ++i;
        }

    }
    printf("Out while loop\n");

    if(strcmp(input, warray[i].str) == 1)
    {
        printf("Inside save if statement\n");

        warray[elements].str = malloc(strlen(input)+1);

        strcpy(warray[elements].str, input);

        warray[elements].wc = 1;

        elements++;
    }


}

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


    if (argc < 3)
    {
        puts("Please supply the input filename and desired output filename as arguments.");
        return 1;
    }

    infile = fopen(argv[1], "r");
    if(infile == NULL)
    {
        printf("File failed to open. Error: %d\n", errno);
        return 1;
    }
    else
    {
        puts("File opened successfully.");
        printFile();
        getWords();
    }

    return 0;

}

我已经输入了一些打印语句来尝试隔离问题,它似乎在这里遇到了问题,在 save(char *input) 函数内:

if(strcmp(input, warray[i].str) == 1)
{
    printf("Inside save if statement\n");

    warray[elements].str = malloc(strlen(input)+1);

    strcpy(warray[elements].str, input);

    warray[elements].wc = 1;

    elements++;
}

我确实有一种感觉,因为我曾要求 strcmp 检查它的值是否 == 1,而我或许应该只检查任何非零值,但我已经尝试过了,我仍然出现分段错误。

如果有人能指出我正确的方向,我将不胜感激,并在此先感谢!

【问题讨论】:

  • 第一件事:使用调试器并确定导致错误的行。检查变量并尝试弄清楚它们是如何获得它们的值的。如果需要,请逐步重新运行,观察每一步的变量。如果不确定如何执行上述任何操作,请询问有关这些操作的问题。

标签: c


【解决方案1】:

您的实现存在几个逻辑缺陷。根据您的代码,我假设您想要执行以下操作:

  • 检查warray 是否为空。如果为空,则分配一个元素。
  • 如果不为空,则检查单词是否已经存在。如果是,则增加计数器。
  • 如果单词不在数组中,则在数组中分配一个新元素并将单词保存在那里。

但您的代码执行以下操作。

if(!warray)
{
    warray = malloc(sizeof(struct word));
    printf("Made array.\n");
}

这部分没问题。

else
{
    printf("New.\n");
    warray = realloc(warray, (elements++)*sizeof(struct word));
}

这不应该在这里。您应该先检查是否重复,然后根据需要进行分配。

while(i < elements)
{
    printf("in while loop\n");
    if(strcmp(input, warray[i].str) == 0)
    {
        warray[i].wc++;
    }
    else
    {
        ++i;
    }
}

这是错误。如果这个词已经存在,那么它将停留在warray[i].wc++; 行。您应该在增加计数器后返回。

if(strcmp(input, warray[i].str) == 1)
{
    printf("Inside save if statement\n");
    warray[elements].str = malloc(strlen(input)+1);
    strcpy(warray[elements].str, input);
    warray[elements].wc = 1;
    elements++;
}

这也是错误。在上一个循环之后,i 的值将等于elements 的值。但是数组索引是从0elements-1。所以warray[i]warray[elements] 都会导致分段错误。 (您之前在warray = realloc(warray, (elements++)*sizeof(struct word)); 线上增加了elements 的值)

注意:函数getwords 中的for(j = 0; j &lt; cw[99]; j++) 也可能导致分段错误。

编辑:我之前没有注意到后增量问题。应该是

warray = realloc(warray, (++elements)*sizeof(struct word));

而不是

warray = realloc(warray, (elements++)*sizeof(struct word));

感谢 Chronos。

【讨论】:

  • 非常感谢!在修复了这些错误并移动了一些东西之后,我的程序现在似乎可以正常运行了。没有更多的分段错误(现在;))!再次感谢!
【解决方案2】:

好的,让我看看能不能帮上忙。快速浏览一下,我发现了三个明显的主要问题!

首先,在getWords 中,在最后一个for 循环(“for(j = 0;...”)中,终止条件是“j &lt; cw[99]”......我怀疑您的意思是“j &lt; 100”。我们不知道 c[99] 中的值是什么,或者输入字符串是否足够长以达到数组的最后一个元素!

其次,在save 中,在第一个 else 子句中,您似乎正试图将 warray 的大小增加一个元素...但是,因为您正在 POST 递减变量 elements,数组未调整大小。如果您改为预先增加 elements,它应该可以解决问题。

warray = realloc(warray, (++elements)*sizeof(struct word));

第三,同样在save 中,您的意图似乎只是增加之前出现过的单词的计数......但是,此时您已经增加了数组的大小,所以您是不必要地占用内存资源。

前两个将导致您的程序访问超出您的程序预期范围的内存,并可能导致您的系统崩溃,或者至少是非常不可预测的系统行为。

可能还有更多,但这应该会让你继续前进......

【讨论】:

    【解决方案3】:

    一个问题是你一直没有重新分配单词:

    int elements = sizeof(warray)/sizeof(struct word);
    

    sizeof(warray) 将是指针的大小,它永远不会改变。因为sizeof(struct word)sizeof(pointer)+padding+sizeof(int),所以你正在执行sizeof(pointer) / (sizeof(pointer)+padding+sizeof(int)),这就像在简单的情况下说4 / (4+0+4)4/8。由于整数除法的规则,每次调用save 函数时,您实际上将elements 设置为0,因此,您正在执行malloc(0),这是未定义的行为。如果它返回NULL,则任何使用warray[i] 的行都可能导致段错误。它可能返回非NULL 值,但返回的指针可能指向未分配的内存。

    save 函数之外存储元素的数量将允许您跟踪数组中的元素数量。

    另外,您的realloc 行是错误的。通过执行elements++,您的意思是如果元素的数量之前为 1,您应该只分配 1,并且elements 在下一个序列点之前的某个时间递增。你想要的是++elements,它会在分配之前增加元素的数量(例如,你有 1,现在你想要 2)。

    可能还有其他错误,但那些是我注意到的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-01-09
      • 1970-01-01
      • 1970-01-01
      • 2020-02-07
      • 2011-09-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多