在C语言中从文本文件读取并将行解析为单词

13

我是C语言和系统编程的初学者。为了一项作业任务,我需要编写一个程序从stdin读取输入,将其分解成单词并使用System V消息队列(例如计数单词)向排序子进程发送单词。我卡在输入部分。我正在尝试处理输入,删除非字母字符,将所有字母单词转换为小写,最后将一行单词拆分为多个单词。到目前为止,我可以打印所有小写字母单词,但是有单词之间的空行,我认为这是不正确的。请问有人能够看一下并给我一些建议吗?

文本文件中的示例:荷马的《伊利亚特》(The Iliad of Homer)项目古腾堡电子书。

我认为正确的输出应该是:

the
project
gutenberg
ebook
of
the
iliad
of
homer
by
homer

但是我的输出结果如下:

project
gutenberg
ebook
of
the
iliad
of
homer
                         <------There is a line there
by
homer

我认为空行是由","和"by"之间的空格引起的。我尝试过像“如果 isspace(c) 则什么也不做”这样的方法,但它没有起作用。我的代码如下。任何帮助或建议都将受到赞赏。

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


//Main Function
int main (int argc, char **argv)
{
    int c;
    char *input = argv[1];
    FILE *input_file;

    input_file = fopen(input, "r");

    if (input_file == 0)
    {
        //fopen returns 0, the NULL pointer, on failure
        perror("Canot open input file\n");
        exit(-1);
    }
    else
    {        
        while ((c =fgetc(input_file)) != EOF )
        {
            //if it's an alpha, convert it to lower case
            if (isalpha(c))
            {
                c = tolower(c);
                putchar(c);
            }
            else if (isspace(c))
            {
                ;   //do nothing
            }
            else
            {
                c = '\n';
                putchar(c);
            }
        }
    }

    fclose(input_file);

    printf("\n");

    return 0;
}

编辑**

我编辑了我的代码,最终得到了正确的输出:

int main (int argc, char **argv)
{
    int c;
    char *input = argv[1];
    FILE *input_file;

    input_file = fopen(input, "r");

    if (input_file == 0)
    {
        //fopen returns 0, the NULL pointer, on failure
        perror("Canot open input file\n");
        exit(-1);
    }
    else
    {
        int found_word = 0;

        while ((c =fgetc(input_file)) != EOF )
        {
            //if it's an alpha, convert it to lower case
            if (isalpha(c))
            {
                found_word = 1;
                c = tolower(c);
                putchar(c);
            }
            else {
                if (found_word) {
                    putchar('\n');
                    found_word=0;
                }
            }

        }
    }

    fclose(input_file);

    printf("\n");

    return 0;
}

6
+1 如果您发布合理的代码。一个建议:perror(输入)。没有文件名的错误消息是很糟糕的事情之一。 - William Pursell
strtok函数可能会有所帮助。 - keety
3个回答

6

我认为你只需要忽略任何非字母字符!isalpha(c),否则将其转换为小写。在这种情况下,你需要跟踪找到的单词。

int found_word = 0;

while ((c =fgetc(input_file)) != EOF )
{
    if (!isalpha(c))
    {
        if (found_word) {
            putchar('\n');
            found_word = 0;
        }
    }
    else {
        found_word = 1;
        c = tolower(c);
        putchar(c);
    }
}

如果您需要处理单词中的撇号,比如“isn't”,那么可以使用下面的方法 -
int found_word = 0;
int found_apostrophe = 0;
    while ((c =fgetc(input_file)) != EOF )
    {
    if (!isalpha(c))
    {
        if (found_word) {
            if (!found_apostrophe && c=='\'') {
                found_apostrophe = 1;
            }
            else {
                found_apostrophe = 0;
                putchar('\n');
                found_word = 0;
            }
                }
    }
    else {
        if (found_apostrophe) {
            putchar('\'');
            found_apostrophe = 0;
        }
        found_word = 1;
        c = tolower(c);
        putchar(c);
    }
}

1
我怀疑你真正想处理所有非字母字符作为分隔符,而不仅仅是处理空格作为分隔符并忽略非字母字符。否则,foo--bar将显示为单个单词foobar,对吧?好消息是,这使事情变得更容易。您可以删除isspace子句,只使用else子句。

同时,无论您是否特别处理标点符号,都存在一个问题:您会为任何空格打印一个换行符。因此,以\r\n\n结尾的行,甚至以.结尾的句子,都将打印一个空白行。解决这个问题的明显方法是跟踪上一个字符或标志,因此只有在先前已经打印了字母时才打印换行符。

例如:

int last_c = 0

while ((c = fgetc(input_file)) != EOF )
{
    //if it's an alpha, convert it to lower case
    if (isalpha(c))
    {
        c = tolower(c);
        putchar(c);
    }
    else if (isalpha(last_c))
    {
        putchar(c);
    }
    last_c = c;
}

但是你真的想把所有标点符号都视为相同吗?问题陈述暗示你这样做,但在现实生活中,这有点奇怪。例如,foo--bar 应该被显示为单独的单词 foobar,但是 it's 真的应该被显示为单独的单词 its 吗?同样,使用 isalpha 作为“单词字符”的规则也意味着,比如说,2nd 将被显示为 nd

因此,如果isascii不是您的用例中区分单词字符和分隔符字符的适当规则,您将不得不编写自己的函数来进行正确的区分。您可以很容易地使用逻辑表达这样的规则(例如,isalnum(c) || c == '\''),也可以使用表格(只需一个包含128个整数的数组,因此函数为c >= 0 && c < 128 && word_char_table[c])。以这种方式做事的另一个好处是,您可以稍后扩展代码以处理Latin-1或Unicode,或处理程序文本(其具有与英语文本不同的单词字符),等等。


请注意,这将停止一些可能需要保留的标点符号,包括连字符和撇号。您可能需要特殊处理这些情况,在某些情况下(即连字符后跟换行符)仍然要将它们丢弃。否则,像“isn't”这样的单词将无法保留其原始表示。 - WhozCraig
@WhozCraig: 是的,因为OP明确要求跳过所有标点符号,所以我选择做同样的事情。但如果他不想这样,就需要额外的代码来解决。我会在答案中添加关于此事的注释。 - abarnert
@Jongware:是的,这里没有一个明显正确的答案;OP必须理解问题并找出哪个答案适合他的目的。考虑到他描述的作业问题的方式,我认为最简单的答案可能是正确的。但他应该仔细思考并决定。所以WhozCraig提出这个问题是正确的。 - abarnert
@abarnert:非常感谢您回答我的问题。是的,您说得对。我想将所有非字母字符都视为分隔符。我使用了您的建议来修复我的代码。所有字母都在一起。有没有办法将一行单词拆分成多个单词?我会编辑我的代码向您展示。再次感谢。 - user2203774
@abarnert:你好。我接受了别人的建议,并修复了我的代码。现在我可以使用所有非字母字符作为分隔符。我将发布我的新代码。如果您不介意,能否看一下并让我知道是否有任何问题。非常感谢。 - user2203774
显示剩余2条评论

0

看起来你是用空格分隔单词,所以我认为只需要

while ((c =fgetc(input_file)) != EOF )
{
    if (isalpha(c))
    {
        c = tolower(c);
        putchar(c);
    }
    else if (isspace(c))
    {
       putchar('\n');
    }
}

也可以工作。只要您的输入文本单词之间不超过一个空格。


输入中有一个单独的逗号未被复制到输出。简要说明了“单词”的概念。尽管简短而简单,易于调整。 - Jongware

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接