C语言 使用链表将字符串替换为另一个字符串或字符有什么问题

i86rm4rw  于 2023-02-07  发布在  其他
关注(0)|答案(2)|浏览(159)

我有一个包含许多字符的链表,我从输入(what is the weather today?)中输入这些字符,并将其替换为另一个字符串(例如,what替换为how,因此得到how is the weather today?)。
但是如果给定的单词彼此紧邻,例如whatwhat,则它将更改为howwhat,而忽略第二部分。
我认为问题出在比较函数中,但是我不知道如何修复它,但是替换的逻辑应该是这样的:
如果我的列表中的单词和需要的单词相同,那么继续迭代到应该改变的单词的下一个节点的位置(不需要的单词)应为(几乎是单词的结尾),然后我创建一个新的链表,其中包含所需单词的字符,并将temp连接到列表的开头,将列表的next连接到需要更改的单词(不需要的单词)的下一个字符所在的位置,这是我在第一个循环中找到的。
也不要烤我的input()函数,我知道它是不安全的,我只是想看看什么是不安全的意思与我自己的眼睛,而我仍然没有什么损失。
下面是代码:

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

struct node { 
    int value_c;
    struct node *next_c;
    struct node *prev_c;
};

typedef struct node string;

int compare(string *head, char *word) {
    int counter = 0;
    string *temp = head;
    for (int i = 0; i < strlen(word); i++) {
        if (temp->value_c == word[i]) {
            temp = temp->next_c;
            counter++;
        }
    }
    if (counter == strlen(word))
        return 1;
    else
        return 0;
}

void print_c(string *head) {
    while (head != NULL) {
        printf("%c", head->value_c);
        head = head->next_c;
    }
}

void append_c(string **head, char thing) {
    string *newNode = (string *)malloc(sizeof(string));

    newNode->value_c = thing;
    newNode->next_c = NULL;
    
    if (*head == NULL) {
        *head = newNode;
        newNode->prev_c = NULL;
        return;
    }
    
    string *temp = *head;

    while (temp->next_c != NULL)
        temp = temp->next_c;

    temp->next_c = newNode;
    newNode->prev_c = temp;
}

string *replace_all1(string *head, char *what, char *with_what) {
    string *temp = head;
    while (temp != NULL) {
        printf("%c ", temp->value_c);
        if (compare(temp, what) == 1) {
            printf("%i ", 1); 
            printf("%c ", temp->value_c);
            string *new = temp;
            for (int i = 0; i < strlen(what) - 1; i++) {
                new = new->next_c;
            }
            string *word = NULL;
            for (int i = 0; i < strlen(with_what); i++) {
                append_c(&word, with_what[i]);
            }

            string *word_temp = word;
            while (word_temp->next_c != NULL) {
                word_temp = word_temp->next_c;
            }

            word_temp->next_c = new->next_c;
            if (temp->prev_c != NULL) {
                temp->prev_c->next_c = word;
            } else {
                head = word;
                print_c(head);
                temp = word;
                print_c(temp);
                word->prev_c = NULL;
            }
        }
        temp = temp->next_c;
    }
    printf("\n");
    return head;
}

string *String(char *str) {
    string *st = NULL;
    int i = 0;
    while (str[i] != '\0') {
        append_c(&st, str[i]);
        i++;
    }
    return st;
}

string *input() {
    char *a = (char *)malloc(sizeof(char));
    scanf("%[^\n]", a); //maximum of 1408
    string *stri = String(a);
    return stri;
    free(a);
}

int main() {
    string *list = NULL;

    string *big_boy_string = input();
    //printf("%c", big_boy_string->value_c);
    //print_c(big_boy_string);
    //printf("\n");
    //printf("%i", compare(big_boy_string, "what"));
    //printf("%i ", len(big_boy_string));

    //printf("\n");
    //print_c(slice(big_boy_string, 1, 10));
    //print_c(replace(big_boy_string, 'h', 'a'));
    //printf("\n");
    //print_c(reverse(big_boy_string));
    print_c(replace_all1(big_boy_string, "a", "b"));
    //getline();
}
epggiuax

epggiuax1#

char *a = (char*) malloc(sizeof(char)); 
scanf("%[^\n]",a); //maximum of 1408

第一条语句只分配1个字节的内存,所以最大值不是1408,而是1,它可以存储一个char,如果是字符串,则可以存储空终止符,但不能存储更多。
接下来,scanf()将写入超出边界的内存,并调用未定义的行为。后续函数都依赖于此未定义的行为,所以我不打算查看它们。
但是,在同一个函数中会出现内存泄漏。

return stri;
free(a);

在释放分配的内存之前执行return。对free()的调用永远不会执行。
malloc()的返回值也会被忽略。如果后续的取消引用发生在NULL指针上,则代码可能会出现未定义的行为。
旁白:强制类型转换没有意义,可能会隐藏一个bug。malloc()和family返回一个void *,它被隐式转换为正确的类型。
回复:也不要烤我的输入()函数,我知道它不安全,我只是想看看什么是不安全的意思与我自己的眼睛。
如果你已经意识到这一点,那么你就不应该问为什么你的代码不工作,你是在依赖未定义的行为(玩火)。

rdrgkggo

rdrgkggo2#

除了input函数外,没有必要看得更远:它有未定义的行为或最坏的类型,因为你试图将输入字符串读入一个非常小的数组,分配给一个字节。你必须首先修复这个问题。既然你知道你的输入字符串的最大长度,你可以使用这个:

string *input(void) {
    char a[1409];
    if (scanf("%1408[^\n]", a) != 1) { //maximum of 1408
        // invalid or missing input
        return NULL;
    }
    scanf(%*[^\n]");  // consume any remaining characters on the input line
    scanf(%*1[\n]");  // consume the newline if present
    return String(a);
}

以下是使用getchar()代替scanf()的替代方案,后者相当棘手且容易出错:

string *input(void) {
    char a[1409];
    int c;
    size_t i = 0;

    while ((c = getchar()) != EOF && c != '\n') {
        if (i + 1 < sizeof(a))
            a[i++] = (char)c;
    }
    if (c == EOF && i == 0) {
        /* end of file without any input */
        return NULL;
    }
    a[i] = '\0';
    return String(a);
}

比较函数不正确:一旦比较失败,它应该返回false,并且必须测试字符串的结尾(temp == NULL):

int compare(const string *head, const char *word) {
    string *temp = head;
    for (size_t i = 0; word[i] != '\0'; i++) {
        if (temp == NULL || temp->value_c != word[i])
            return 0;
        temp = temp->next_c;
    }
    return 1;
}

replace_all1()函数也有问题:

  • 如果what是空字符串,for (int i = 0; i < strlen(what) - 1; i++)将导致未定义的行为,因为strlen(what) - 1在这种情况下与值SIZE_MAX无符号,导致循环进行很长时间,远远超过new所指向的列表的结尾。
  • 如果被替换的字为空,则while (word_temp->next_c != NULL)将导致未定义的行为,因为word_temp将是NULL
  • 一旦替换了子列表,就不能正确地更新temp以指向被替换节点之后的节点,这可以通过将temp设置为word_temp来实现。
  • 该函数不释放被替换的子列表。

以下是修改后的版本:

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

struct node {
    int value_c;
    struct node *next_c;
    struct node *prev_c;
};

typedef struct node string;

void string_append_char(string **head, int c) {
    string *node = malloc(sizeof(*node));
    if (node == NULL) {
        fprintf(stderr, "out of memory\n");
        exit(1);
    }
    node->value_c = c;
    node->next_c = NULL;

    if (*head == NULL) {
        node->prev_c = NULL;
        *head = node;
    } else {
        string *temp = *head;

        while (temp->next_c != NULL)
            temp = temp->next_c;

        node->prev_c = temp;
        temp->next_c = node;
    }
}

string *string_new(const char *str) {
    string *st = NULL;
    for (int i = 0; str[i] != '\0'; i++) {
        string_append_char(&st, str[i]);
    }
    return st;
}

string *string_input(const char *prompt) {
    string *st = NULL;
    int c;

    if (prompt) {
        printf("%s", prompt);
    }
    while ((c = getchar()) != EOF && c != '\n') {
        string_append_char(&st, c);
    }
    return st;
}

void string_print(const char *before, const string *head, const char *after) {
    printf("%s", before);
    while (head != NULL) {
        putchar(head->value_c);
        head = head->next_c;
    }
    printf("%s", after);
}

void string_free(string *head) {
    while (head != NULL) {
        string *next = head->next_c;
        free(head);
        head = next;
    }
}

int string_compare(const string *head, const char *word) {
    const string *temp = head;
    for (size_t i = 0; word[i] != '\0'; i++) {
        if (temp == NULL || temp->value_c != word[i])
            return 0;
        temp = temp->next_c;
    }
    return 1;
}

int string_replace(string **head, const char *what, const char *with_what) {
    int count = 0;

    if (*what == '\0')
        return 0;

    string *temp = *head;
    while (temp != NULL) {
        if (string_compare(temp, what)) {
            count++;
            // locate the last node of the substring
            string *temp_end = temp;
            for (size_t i = 0; what[i + 1] != '\0'; i++) {
                temp_end = temp_end->next_c;
            }
            string *next = temp_end->next_c;
            if (*with_what == '\0') {
                // just delete the substring
                if (temp->prev_c != NULL) {
                    temp->prev_c->next_c = next;
                } else {
                    *head = next;
                }
                if (next) {
                    next->prev_c = temp->prev_c;
                }
            } else {
                // create a string from the replacement
                string *word = string_new(with_what);
                // locate the last node of the new substring
                string *word_end = word;
                while (word_end->next_c != NULL) {
                    word_end = word_end->next_c;
                }
                word->prev_c = temp->prev_c;
                if (temp->prev_c != NULL) {
                    temp->prev_c->next_c = word;
                } else {
                    *head = word;
                }
                word_end->next_c = next;
                if (next) {
                    next->prev_c = word_end;
                }
            }
            temp_end->next_c = NULL;
            string_free(temp);
            temp = next;
        } else {
            temp = temp->next_c;
        }
    }
    return count;
}

int main() {
    string *list = string_input("enter string: ");
    string_print("input: ", list, "\n");
    printf("replacing 'what' to 'how': %d matches\n", string_replace(&list, "what", "how"));
    string_print("rep1: ", list, "\n");
    printf("replacing 'a' to 'b': %d matches\n", string_replace(&list, "a", "b"));
    string_print("rep2: ", list, "\n");
    printf("deleting 'h': %d matches\n", string_replace(&list, "h", ""));
    string_print("rep3: ", list, "\n");
    string_free(list);
    return 0;
}

示例会话:

enter string: what is the weather today?
input: what is the weather today?
replacing 'what' to 'how': 1 matches
rep1: how is the weather today?
replacing 'a' to 'b': 2 matches
rep2: how is the webther todby?
deleting 'h': 3 matches
rep3: ow is te webter todby?

相关问题