Создание списка слов с последующим удалением дубликатов последнего слова в Си

Дана задача:

Описать тип "список слов" на Си. Список реализовать как цепочку динамических объектов, создаваемых стандартными функциями malloc() или calloc(). Написать программу, которая вводит строку, состояющую из непустых слов, разделенных последовательностями пробелов и табуляций, и строит список из этих слов. Слова размещать в памяти как динамические объекты. Далее из списка исключить все слова, совпадающие с последним (самое последнее остается). Список может быть пустым, если в строке не было слов, тогда результат по определению пустой. Преобразованный список слов напечатать в stdout, отделяя слова одним пробелом. В конце списка - перевод строки

Удалось реализовать динамическую строку. Возникли проблемы с разбиением. Примеры использования функции strtok позволяют лишь однократно использовать динамическую строку, а затем к ней невозможно обратиться. Как концептуально выполнить задачу? Как организовать хранение разделенных по пробелам слов? Как затем искать повторяющиеся слова?

Мое незаконченное решение:

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

 
 
/*Функция посимвольного ввода динамической строки*/ 
char *get_string() {
    int len = 0; // изначально строка пуста
    int capacity = 1; // Нуль-терминатор
    char *str = (char*) malloc(sizeof(char)); // динамическая пустая строка
    char ch = getchar(); // символ для чтения данных

    // посимвольное чтение до перевода строки
    while ( ch != '\n') {
        str[len++] = ch; 
        /*Увеличение зарезервированной памяти при превышении*/
        if (len >= capacity) {
            capacity *= 2; // увеличиваем ёмкость строки в два раза
            str = (char*) realloc(str, capacity * sizeof(char)); 
        }
        ch = getchar(); // чтение символа из потока         
    }
    return str; 
}

int main() {
    char *str = get_string(); // считываем динамическую строку
    char * tokens;
    int len = strlen(str), i=0, j;
    char list[len][30];
    
    printf("Введена строка: '%s'\n", str); 
    
    tokens = strtok(str," ");
    while (tokens != NULL){
    for (j=0; j<strlen(tokens); j++)
        list[i][j]=tokens[j];
    i++;
    tokens = strtok(NULL, " ");
    }
    printf("Введена строка: '%s'\n", list[2]); 
    

    return 0;
}

Ответы (1 шт):

Автор решения: Eugene X
  1. Функций чтения строки в Си навалом, начиная от прямого взаимодействия с консолью, заканчивая intrrupt'ами
  2. Если ты в программе используешь русские буквы, то переведи программу на wchar_t это избавит тебя от огромного количества геморроя.
  3. Есть несколько реализаций бесконечного массива в Cи. Во первых это 00 строка "слово\0слово\0слово\0\0" пока не найдёшь пустую строку, во вторых это бесконечный nextPtr то есть некая структура которая оставляет данные и ссылается на следующее слово.

Вот рабочий пример написал на чистом линуксовом C минут за 20-30. Можешь проверить его на www.onlinegdb.com/online_c_compiler

ps: Комментарии принимаются.

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

// zero terminated char* buffer
#define ztword char*

typedef struct {
    ztword word;
    void* next;
} InfineMem;


InfineMem* first_simbol = NULL;


char newOrAdd(InfineMem* chunk, ztword word) {
    InfineMem* owner;
    while (chunk != NULL) {
        if (strcmp(chunk->word, word) == 0) {
            return 1; // если слово уже есть.
        }
        owner = chunk;
        chunk = chunk->next;
    }
    owner->next = (InfineMem*) malloc(sizeof(InfineMem));
    ((InfineMem*) owner->next)->word = (char*) calloc(strlen(word) + 1, sizeof(char));
    strcpy(((InfineMem*) owner->next)->word, word);
    ((InfineMem*) owner->next)->next = NULL;
    return 0;
}

void memory_cleaner(InfineMem* chunk) {
    while (chunk != NULL) {
        InfineMem* next = chunk->next;
        if (chunk->word != NULL) {
            free(chunk->word);
        }
        free(chunk);    
        chunk = next;
    }
}

void printWordDatabase(InfineMem* chunk) {
    while (chunk != NULL) {
        printf("$ %s\n", chunk->word);
        chunk = chunk->next;
    }
}

int main()
{
    printf("Enter word or 'quit' to stop or 'print' to print all words in db\n");
    char* input = calloc(1024, sizeof(char));

    memset(input, 0, 1024);
    while (strcmp(input, "quit") != 0) {
        printf("Word? ");
        fgets(input, 1024, stdin);
        input[strlen(input)-1] = '\0'; // cut trailing '\n'
        if (strcmp(input, "print") == 0) {
            printWordDatabase(first_simbol);
        }
        else if (first_simbol == NULL) {
            first_simbol = malloc(sizeof(InfineMem));
            first_simbol->word = (char*) calloc(strlen(input) + 1, sizeof(char));
            strcpy(first_simbol->word, input);
            first_simbol->next = NULL;
            printf("New: %s\n", input);
        }
        else if (newOrAdd(first_simbol, input) == 1) {
            printf("Error: %s - already exists!\n", input);
        }
        else {
            printf("New: %s\n", input);
        }
    }

    memory_cleaner(first_simbol);
    free(input);
    return 0;
}
→ Ссылка