字数应用 - C.

时间:2014-02-23 10:06:21

标签: c

我目前正在尝试编写一个应用程序来计算ASCII文件中出现的单词数(删除标点符号并忽略空格)。应用程序应将单词和单词count存储在数据结构中,最终将按降序排序,然后打印到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,或许我应该检查是否有任何非零值,但我已经尝试过了,而且我是仍然会出现分段错误。

如果有人能指出我正确的方向,我会很感激,并提前感谢!

3 个答案:

答案 0 :(得分:2)

好的,让我看看能否提供帮助。快速浏览,我看到三个明显的主要问题!

首先,在getWords中,在最后一个for循环(“for(j = 0;...”)中,终端条件为“j < cw[99]”...我怀疑你的意思是“{{1 }}”。我们不知道c [99]中的值是什么,或者输入字符串是否足够长,以使REACHED成为数组的最后一个元素!

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

elements

第三,同样在warray = realloc(warray, (++elements)*sizeof(struct word)); 中,您的意图似乎只是增加先前出现过的单词的数量......但是,您已经增加了该数组的大小,所以你不必要地耗尽内存资源。

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

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

答案 1 :(得分:2)

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

  • 检查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]都会导致细分错误。 (您在行elements之前增加warray = realloc(warray, (elements++)*sizeof(struct word));的值)

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

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

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

而不是

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

感谢Chronos。

答案 2 :(得分:1)

一个问题是您不断重新分配任何字词:

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一样。由于整数除法的规则,每次调用elements函数时,您都有效地将save设置为0,因此,您正在执行malloc(0),这是未定义的行为。如果它返回NULL,则使用warray[i]的任何行都可能导致段错误。它可能返回非NULL值,但返回的指针可能指向未分配的内存。

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

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

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