Чтение из текстового файла и разбор строк в слова в C

Я начинаю работать в C и системном программировании. Для задания домашней работы мне нужно написать программу, которая считывает входные данные из строк разбора строк stdin в слова и отправляет слова в подпроцессы сортировки, используя очереди сообщений System V (например, количество слов). Я застрял на входной части. Я пытаюсь обработать вход, удалить не-альфа-символы, поместить все альфа-слова в нижний регистр и, наконец, разделить строку слов на несколько слов. Пока я могу печатать все альфа-слова в нижнем регистре, но между словами есть строки, которые, я считаю, неверны. Может кто-нибудь взглянуть и дать мне несколько предложений?

Пример из текстового файла: Проект Gutenberg EBook "Илиады Гомера" Гомера

Я думаю, что правильный вывод должен быть:

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". Я пробовал такие вещи, как "if 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 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;
}

EDIT **

Я редактировал свой код и, наконец, получил правильный вывод:

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 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;
}

Ответ 1

Я думаю, что вам просто нужно игнорировать любой альфа-символ! 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);
    }
}

Если вам нужно обрабатывать апострофы внутри слов, таких как "нет", тогда это должно сделать это.

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);
    }
}

Ответ 2

Я подозреваю, что вы действительно хотите обрабатывать все не-алфавитные символы в качестве разделителей, а не просто обрабатывать пробелы как разделители и игнорировать не-алфавитные символы. В противном случае foo--bar будет отображаться как одно слово foobar, правильно? Хорошей новостью является то, что облегчает жизнь. Вы можете удалить предложение isspace и просто использовать предложение else.

Между тем, независимо от того, лечите ли вы пунктуации или нет, у вас есть проблема: вы печатаете новую строку для любого места. Итак, строка, заканчивающаяся \r\n или \n, или даже предложение, заканчивающееся на ., напечатает пустую строку. Очевидным способом этого является отслеживание последнего символа или флага, поэтому вы печатаете только строку новой строки, если вы ранее печатали письмо.

Например:

int last_c = 0

while ((c = fgetc(input_file)) != EOF )
{
    //if it 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 должен, вероятно, отображаться как отдельные слова foo и bar, но должен ли it's действительно отображаться как отдельные слова it и s? В этом случае использование isalpha в качестве правила для "символов слова" также означает, что, скажем, 2nd будет отображаться как nd.

Итак, если isascii не является подходящим правилом для вашего случая использования, чтобы отличать слова от символов разделителя, вам придется написать свою собственную функцию, которая делает правильное различие. Вы можете легко выразить такое правило в логике (например, isalnum(c) || c == '\'') или с таблицей (всего лишь массив из 128 int, поэтому функция c >= 0 && c < 128 && word_char_table[c]). Выполнение таких действий имеет дополнительное преимущество, которое вы можете впоследствии расширить свой код, чтобы иметь дело с латинским-1 или Unicode, или обрабатывать текст программы (который имеет разные словарные символы, чем текст на английском языке) или...

Ответ 3

Кажется, что вы разделяете слова пробелами, поэтому я думаю, что просто

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

тоже будет работать. Если у вашего входного текста будет не более одного пробела между словами.