【问题标题】:C - Char array SEEMS to copy, but only within loop scopeC - Char 数组似乎要复制,但仅限于循环范围内
【发布时间】:2015-09-27 22:11:55
【问题描述】:

现在,我正在尝试通过编写一个函数来熟悉 C,给定一个字符串,该函数将用新的子字符串替换目标子字符串的所有实例。但是,我遇到了重新分配 char* 数组的问题。在我看来,似乎我能够在主循环结束时成功地将数组string 重新分配到所需的新大小,然后执行strcpy 以用更新的字符串填充它。但是,在以下情况下它会失败:

字符串的原始输入:“使用洗手间。然后我需要”
要替换的目标:“the”(不区分大小写)
期望的替换值:“th'”

在循环结束时,printf("result: %s\n ",string); 行打印出正确的短语“use th' restroom. Then I need”。但是,string 似乎随后会自行重置:在while() 语句中对strcasestr 的调用成功,循环开头的行printf("string: %s \n",string); 打印原始输入字符串,并且循环无限期地继续。

任何想法都将不胜感激(我提前为我的调试printf 语句道歉)。谢谢!

函数代码如下:

int replaceSubstring(char *string, int strLen, char*oldSubstring,
    int oldSublen, char*newSubstring, int newSublen )
{
    printf("Starting replace\n");
    char* strLoc;

    while((strLoc = strcasestr(string, oldSubstring)) != NULL )
    {
        printf("string: %s \n",string);
        printf("%d",newSublen);
        char *newBuf = (char *) malloc((size_t)(strLen +
            (newSublen - oldSublen)));
        printf("got newbuf\n");
        int stringIndex = 0;
        int newBufIndex = 0;
        char c;
        while(true)
        {
            if(stringIndex > 500)
                break;
            if(&string[stringIndex] == strLoc)
            {
                int j;
                for(j=0; j < newSublen; j++)
                {

                    printf("new index: %d  %c --> %c\n",
                        j+newBufIndex, newBuf[newBufIndex+j], newSubstring[j]);
                    newBuf[newBufIndex+j] = newSubstring[j];
                }
                stringIndex += oldSublen;
                newBufIndex += newSublen;
            }
            else
            {
                printf("old index: %d  %c --> %c\n", stringIndex,
                    newBuf[newBufIndex], string[stringIndex]);
                newBuf[newBufIndex] = string[stringIndex];
                if(string[stringIndex] == '\0')
                    break;
                newBufIndex++;
                stringIndex++;
            }
        }
        int length = (size_t)(strLen + (newSublen - oldSublen));
        string = (char*)realloc(string,
            (size_t)(strLen + (newSublen - oldSublen)));
        strcpy(string, newBuf);
        printf("result: %s\n ",string);
        free(newBuf);
    }
    printf("end result: %s ",string);
}

【问题讨论】:

  • 代码中的每一次强制转换都是多余的和/或引入错误;把它们拿出来
  • 为了改善您的问题,发布MCVE 显示使用导致问题的参数调用此函数,并显示您获得的输出。例如,也许您为 strLen 参数传递了错误的值,我们无法从这个 sn-p 中分辨出来。
  • 你没有为空终止符分配足够的内存
  • strcasestr 无论如何都会忽略strLen 所以如果你的目标是处理计数字符串,这个函数不会实现它。如果您的目标是处理以空字符结尾的字符串,那么它会引入额外的失败点,要求调用者也传递正确的长度;该函数应根据需要调用strlen
  • 这个函数在realloc之后永远不会对string做任何事情:你做了printf("end result: %s ",string);但永远不会把它返回给调用者或任何东西。我希望调用代码不会尝试重新使用您作为第一个参数传入的指针...(该内存块已被 realloc 调用释放)。

标签: c arrays regex string malloc


【解决方案1】:

首先,应就所需的行为和界面阐明任务。

主题“字符数组...”不清楚。 您提供了strLenoldSublennewSublen,所以看起来您确实只想使用给定长度的大容量内存缓冲区。 但是,您使用strcasestrstrcpystring[stringIndex] == '\0' 并提及printf("result: %s\n ",string);。 因此,我假设您想使用“null 终止字符串”,调用者可以将其作为字符串文字传递:“abc”。 不需要将所有这些长度传递给函数。

看起来您正在尝试实现递归字符串替换。每次更换后,您都从头开始。 让我们考虑更复杂的参数集,例如,将abaaba 中的aba 替换为ab

案例 1:单次通过输入流

Each of both old substrings can be replaced: "abaaba" => "abab"

That is how the standard sed string replacement works:

> echo "abaaba" | sed 's/aba/ab/g'
abab

案例2:考虑可能重叠的递归替换

The first replacement: "abaaba" => "ababa"
The second replacement in already replaced result: "ababa" => "abba"

请注意,这种情况是不安全的,例如将“loop”替换为“loop loop”。这是一个无限循环。

假设我们要实现一个函数,该函数接受以空字符结尾的字符串,并且与 sed 一样一次性完成替换。

一般来说,替换不能代替输入字符串(在同一内存中)。 请注意,realloc 可能会使用新地址分配新的内存块,因此您应该将该地址返回给调用者。

为了实现简单,可以在内存分配之前计算结果所需的空间(案例 1 实现)。所以不需要重新分配:

#define _GNU_SOURCE

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

char* replaceSubstring(const char* string, const char* oldSubstring,
    const char* newSubstring)
{
    size_t strLen = strlen(string);
    size_t oldSublen = strlen(oldSubstring);
    size_t newSublen = strlen(newSubstring);

    const char* strLoc = string;
    size_t replacements = 0;

    /* count number of replacements */
    while ((strLoc = strcasestr(strLoc, oldSubstring)))
    {
        strLoc += oldSublen;
        ++replacements;
    }

    /* result size: initial size + replacement diff + sizeof('\0') */
    size_t result_size = strLen + (newSublen - oldSublen) * replacements + 1;

    char* result = malloc(result_size);

    if (!result)
        return NULL;

    char* resCurrent = result;
    const char* strCurrent = string;
    strLoc = string;

    while ((strLoc = strcasestr(strLoc, oldSubstring)))
    {
        memcpy(resCurrent, strCurrent, strLoc - strCurrent);
        resCurrent += strLoc - strCurrent;
        memcpy(resCurrent, newSubstring, newSublen);
        resCurrent += newSublen;
        strLoc += oldSublen;
        strCurrent = strLoc;
    }

    strcpy(resCurrent, strCurrent);

    return result;
}

int main()
{
    char* res;

    res = replaceSubstring("use the restroom. Then I need", "the", "th");
    printf("%s\n", res);
    free(res);

    res = replaceSubstring("abaaba", "aba", "ab");
    printf("%s\n", res);
    free(res);

    return 0;
}

【讨论】:

  • 角落案例:可能会被while ((strLoc = strcasestr(strLoc, ""))) 卡住。否则,很好的代码。
  • @chux 谢谢!好观察!我只是想编辑我的帖子以添加对"" 的验证以返回NULL,因为sed 在空输入模式的情况下也会返回错误,但是当我仔细考虑它时,我注意到它可能取决于要求.提供有效输入可能是调用者的责任(就像所有输入指针不能是NULL 并且必须指向有效字符串一样)。当然,调用者应该知道空输入模式的“未定义”行为,当然调用者还应该检查返回的结果是否为NULL
猜你喜欢
  • 2012-04-27
  • 1970-01-01
  • 1970-01-01
  • 2014-03-30
  • 2019-09-08
  • 2015-08-08
  • 2010-12-20
  • 1970-01-01
  • 2015-10-17
相关资源
最近更新 更多