Набор символов в формате списка разбить в список чисел и список слов - C (СИ)
Формулировка задачи:
Всем добрый день!
Мне дали задачу написать программу, в которую вводится набор символов в формате списка. после обработки этого списка должно получиться 2 списка: список чисел и список слов (напр. вводится: qwert123 43yt, на выходе должны получиться: список чисел 123 43, список слов: qwert yt). Я смог реализовать эту программу, но для решения этой задачи я использовал выделение mallocом памяти под новые элементы списка и просто копировал туда элементы. Препод такое решение забраковал. Сказал, что необходимо модифицировать исходный список, т.е. берется элемент из списка и переносится в другой список и никаких дополнительных выделений памяти...
Подскажите, как сделать правильно работающую программу?
Прикладываю свой вариант решения:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <stdbool.h>
typedef struct Item
{
char c;
struct Item *next;
} Item;
/* Ввод строки - списка */
int getList(Item **);
/* Вывод строки - списка */
void putList(const char *, Item *);
/* Освобождение памяти */
Item *deleteList(Item *);
/* Формирование новой строки - списка */
Item *newStringWord(Item *);
Item *newStringNum(Item *);
/* удаление пробелов из строки - списка */
Item *delSpace(Item *);
/* пропуск слова в строке - списке */
Item *skipWord(Item *);
/* удаление слова из строки - списка */
Item *delWord(Item *);
bool isalpha(char);
bool isdigit(char);
/* Функция для ввода строки - списка.
** Функция возвращает указатель на список, в котором размещена введенная строка.
** По условиям задачи, может быть введена пустая строка, тогда
** функция возвращает значение NULL - пустой список.
** Функция должна отслеживать состояние "Конец файла", и это состояние нельзя
** связать со значением указателя NULL - пустая строка.
** Поэтому предлагаемая функция с прототипом int getList(Item **) возвращает
** результат 1, если строка введена, или 0, если обнаружен конец файла,
** а саму строку возвращает через параметр функции.
*/
int getList(Item **pptr)
{
char buf[21], *str;
Item head = { '*', NULL };
Item *last = &head;
int n, rc = 1;
do
{
n = scanf("%20[^\n]", buf);
if (n < 0)
{
deleteList(head.next);
head.next = NULL;
rc = 0;
continue;
}
if (n > 0)
{
for (str = buf; *str != '\0'; ++str)
{
last->next = (Item *)malloc(sizeof(Item));
last = last->next;
last->c = *str;
}
last->next = NULL;
}
else
{
scanf("%*c");
}
} while (n > 0);
*pptr = head.next;
return rc;
}
/* Вывод строки - списка */
void putList(const char *msg, Item *ptr)
{
printf("%s: "", msg);
for (; ptr != NULL; ptr = ptr->next)
{
printf("%c", ptr->c);
}
printf(""\n");
}
/* Освобождение памяти */
Item *deleteList(Item *ptr)
{
Item *tmp = NULL;
while (ptr != NULL)
{
tmp = ptr;
ptr = ptr->next;
free(tmp);
}
return ptr;
}
bool isalpha(char c) {
if ((c >= 'a') && (c <= 'z') || (c >= 'A') && (c <= 'Z'))
return true;
return false;
}
bool isdigit(char c) {
if ((c >= '0') && (c <= '9'))
return true;
return false;
}
int main()
{
Item *ptr = NULL;
Item *pword = NULL;
Item *pnum = NULL;
while (puts("enter string"), getList(&ptr))
{
putList("Entered string", ptr);
pword = newStringWord(ptr);
putList("Result string 1", pword);
pnum = newStringNum(ptr);
putList("Result string 2", pnum);
ptr = deleteList(ptr);
pword = deleteList(pword);
pnum = deleteList(pnum);
}
return 0;
}
/* Формирование новой строки - списка */
Item *newStringWord(Item *p)
{
char bChar;
char eChar;
Item head = { '*', NULL };
Item head2 = { '*', p };
Item *cur = &head2;
Item *last = &head, *prev = &head; /* prev - для корректной обработки конца списка */
int fl = 0;
/* Используется список с головным элементом; поэтому cur указывает
** на элемент списка, предшествующий тому, который будет анализироваться
*/
while (cur->next)
{
/* здесь надо анализировать и cur, так как в результате пропуска
** слова cur указывает на элемент списка, следующий за последним символом
** слова, а его может и не быть - последний символ слова есть последний
** символ списка
*/
cur = cur->next;
if (isalpha(cur->c))
{
last->next = (Item *)malloc(sizeof(Item));
last = last->next;
last->c = cur->c;
last->next = NULL;
prev = last;
fl = 1;
}
else if (fl)
{
last->next = (Item *)malloc(sizeof(Item));
last = last->next;
last->c = ' ';
last->next = NULL;
fl = 0;
}
}
/* возможно, в конце строки - списка остался лишний пробел */
if (prev->next)
{
free(prev->next);
prev->next = NULL;
}
return head.next;
}
Item *newStringNum(Item *p)
{
char bChar;
char eChar;
Item head = { '*', NULL };
Item head2 = { '*', p };
Item *cur = &head2;
Item *last = &head, *prev = &head; /* prev - для корректной обработки конца списка */
int fl = 0;
/* Используется список с головным элементом; поэтому cur указывает
** на элемент списка, предшествующий тому, который будет анализироваться
*/
while (cur->next)
{
/* здесь надо анализировать и cur, так как в результате пропуска
** слова cur указывает на элемент списка, следующий за последним символом
** слова, а его может и не быть - последний символ слова есть последний
** символ списка
*/
cur = cur->next;
if (isdigit(cur->c))
{
last->next = (Item *)malloc(sizeof(Item));
last = last->next;
last->c = cur->c;
last->next = NULL;
prev = last;
fl = 1;
}
else
if (fl)
{
last->next = (Item *)malloc(sizeof(Item));
last = last->next;
last->c = ' ';
last->next = NULL;
fl = 0;
}
}
/* возможно, в конце строки - списка остался лишний пробел */
if (prev->next)
{
free(prev->next);
prev->next = NULL;
}
return head.next;
}
/* удаление пробелов из строки - списка
** результат - указатель на элемент списка, содержащего
** непробельный символ, или NULL
*/
Item *delSpace(Item *p)
{
Item *tmp;
while (p && (p->c == ' ' || p->c == '\t'))
{
tmp = p;
p = p->next;
free(tmp);
}
return p;
}
/* пропуск слова в строке - списке
** результат - указатель в исходном списке на
** послений символ слова
*/
Item *skipWord(Item *p)
{
while (p->next && p->next->c != ' ' && p->next->c != '\t')
{
p = p->next;
}
return p;
}
/* удаление слова из строки - списка
** результат - указатель на элемент списка, содержащего
** пробельный символ, или NULL
*/
Item *delWord(Item *p)
{
Item *tmp;
while (p && p->c != ' ' && p->c != '\t')
{
tmp = p;
p = p->next;
free(tmp);
}
return p;
}Решение задачи: «Набор символов в формате списка разбить в список чисел и список слов»
textual
Листинг программы
Item *item = list; // исходный список
Item *words=NULL; // начало списка, содержащего буквы
Item *wordsEnd=NULL; // конец списка, содержащего буквы
Item *numbers=NULL, *numbersEnd=NULL;
while (item)
{
Item *next = item->next;
if (isdigit(item->c))
{
// нужно ещё отдельно учесть случай wordsEnd == NULL
wordsEnd->next = item;
wordsEnd = item;
}
else if (isalpha(item->c))
{
numbersEnd->next = item;
numbersEnt = item;
}
item = next;
}
list = NULL;
Объяснение кода листинга программы
Предполагая, что входной список представлен в виде последовательности чисел и слов, код выполняет следующие действия:
- Создает три указателя:
itemуказывает на текущий элемент входного списка,wordsуказывает на начало списка слов (букв),wordsEndуказывает на конец списка слов. Указательnumbersуказывает на начало списка чисел, а указательnumbersEndуказывает на конец списка чисел. - В цикле
whileперебирает элементы входного списка. Для каждого элемента выполняется следующее:- Создается новый указатель
next, который указывает на следующий элемент в списке. - Если текущий элемент является числом (проверка
isdigit(item->c)), он добавляется в список слов, начиная сwordsEnd. В этом случаеwordsEndобновляется, чтобы указывать на последний добавленный элемент. - Если текущий элемент является буквой (проверка
isalpha(item->c)), он добавляется в список чисел, начиная сnumbersEnd. В этом случаеnumbersEndобновляется, чтобы указывать на последний добавленный элемент.
- Создается новый указатель
- После завершения цикла, все указатели обнуляются, что означает, что список слов и список чисел теперь являются независимыми списками. Вот список действий в виде нумерованного списка с названиями и значениями переменных:
Item *item = list;- Инициализирует указательitemдля первого элемента входного списка.Item *words=NULL;- Инициализирует указательwordsдля начала списка слов (букв) с значениемNULL.Item *wordsEnd=NULL;- Инициализирует указательwordsEndдля конца списка слов (букв) с значениемNULL.Item *numbers=NULL, *numbersEnd=NULL;- Инициализирует указательnumbersдля начала списка чисел с значениемNULL, и обновляетnumbersEndдля указания на конец списка чисел.while (item)- Начинает цикл, который перебирает элементы входного списка.Item *next = item->next;- Создает новый указательnextдля следующего элемента в списке.if (isdigit(item->c))- Если текущий элемент является числом, выполняется следующая операция.wordsEnd->next = item;- Добавляет текущий элемент в список слов (букв), начиная сwordsEnd.wordsEnd = item;- ОбновляетwordsEnd, чтобы указывать на последний добавленный элемент в списке слов (букв).else if (isalpha(item->c))- Если текущий элемент является буквой, выполняется следующая операция.numbersEnd->next = item;- Добавляет текущий элемент в список чисел, начиная сnumbersEnd.numbersEnt = item;- ОбновляетnumbersEnt, чтобы указывать на последний добавленный элемент в списке чисел.item = next;- Переходит к следующему элементу входного списка.list = NULL;- После завершения цикла, обнуляет указательlist, что означает, что список слов и список чисел теперь являются независимыми списками. Это интерпретация кода на основе предположений о постановке задачи и синтаксисе языка программирования.