将字符串插入C

时间:2017-10-26 15:20:34

标签: c string malloc ansi-c

我实现了一个函数,给定一个字符串,一个字符和另一个字符串(因为现在我们可以称之为" substring");将字符串放在字符串中的字符串的任何位置。 为了更好地解释我,给定这些参数,这就是函数应返回的内容(伪代码):

func ("aeiou", 'i', "hello")  ->  aehelloou

我正在使用string.h lib中的一些函数。我测试了它的效果非常好:

char *somestring= "this$ is a tes$t wawawa$wa";
printf("%s", strcinsert(somestring, '$', "WHAT?!") );

Outputs:    thisWHAT?! is a tesWHAT?!t wawawaWHAT?!wa

所以现在一切都很好。问题是当我尝试用同样的方法时,例如这个字符串:

char *somestring= "this \"is a test\" wawawawa";
printf("%s", strcinsert(somestring, '"', "\\\"") );

因为我想更改"的每个\"。当我这样做时,PC崩溃了。我不知道为什么但它停止工作然后关机。我已经了解了string.h lib的某些功能的不良行为,但我找不到任何有关此信息的信息,我真的感谢任何帮助。

我的代码:

#define salloc(size) (str)malloc(size+1) //i'm lazy
typedef char* str;

str strcinsert (str string, char flag, str substring)
{
    int nflag= 0; //this is the number of times the character appears
    for (int i= 0; i<strlen(string); i++)
        if (string[i]==flag)
            nflag++;
    str new=string;
    int pos;
    while (strchr(string, flag)) //since when its not found returns NULL
    {
        new= salloc(strlen(string)+nflag*strlen(substring)-nflag);
        pos= strlen(string)-strlen(strchr(string, flag));
        strncpy(new, string, pos);
        strcat(new, substring);
        strcat(new, string+pos+1);
        string= new;      
    }
    return new;
}

感谢您的帮助!

4 个答案:

答案 0 :(得分:1)

在第二个循环中,您始终在字符串中查找第一个flag字符。在这种情况下,这将是您刚从substring插入的那个。 strchr函数将始终找到该引用并且永远不会返回NULL,因此您的循环将永远不会终止并且只是继续分配内存(并且不够,因为您的字符串会随意变大)。

说到分配内存,你需要更加小心。与Python不同,C不会在您不再使用内存时自动注意到;您malloc 必须 free d的任何内容。您还可以分配比您需要的内存更多的内存:即使在您的工作"this$ is a tes$t wawawa$wa"示例中,您也可以为循环的每次迭代上的完整字符串分配足够的空间,而不是free任何一个。你应该在第二个循环之前运行一次分配。

这不像 那样重要,但你也应该注意性能。对strcatstrlen的每次调用都会遍历整个字符串,这意味着您可以比您需要的更频繁地查看它。您应该保存strlen的结果,并将新字符串直接复制到您知道NUL终结符的位置。同样适用于strchr;你已经替换了字符串的开头,并且不想浪费时间再次查看它,除了导致你当前错误的部分。

与这些问题相比,使用typedef和macro的评论中提到的样式问题相对较小,但仍然值得一提。 C中的char*与Python中的str不同;尝试将typedef改为同名,只会让你更有可能尝试将它们视为同一个并遇到这些问题。

答案 1 :(得分:1)

  

我不知道为什么会停止工作

strchr(string, flag)正在查看标志的整个字符串。搜索需要限制为尚未检查/更新的字符串部分。通过重新搜索部分替换字符串,代码一遍又一遍地找到flag

整个字符串管理方法需要重新工作。由于OP报告了Python背景,我发布了一种非常简单的C方法,因为模仿Python并不是一个好方法。 C在记忆管理方面尤其不同。

未经测试的代码

// Look for needles in a haystack and replace them
// Note that replacement may be "" and result in a shorter string than haystack
char *strcinsert_alloc(const char *haystack, char needle, const char *replacment) {
  size_t n = 0;
  const char *s = haystack;
  while (*s) {
    if (*s == needle) n++;  // Find needle count
    s++;
  }
  size_t replacemnet_len = strlen(replacment);
  //                        string length  - needles + replacements      + \0
  size_t new_size = (size_t)(s - haystack) - n*1     + n*replacemnet_len + 1;
  char *dest = malloc(new_size);
  if (dest) {
    char *d = dest;
    s = haystack;
    while (*s) {
      if (*s == needle) {
        memcpy(d, s, replacemnet_len);
        d += replacemnet_len;
      } else {
        *d = *s;
        d++;
      }
      s++;
    }
    *d = '\0';
  }
  return dest;
}

答案 2 :(得分:1)

在你的程序中,你面临输入问题 -

char *somestring= "this \"is a test\" wawawawa";

因为您希望将"替换为\"

第一个问题是,在"中替换\"的{​​{1}}时,在下一次迭代string中会找到strchr(string, flag)的最后一次插入" 1}}。因此,在后续的交互中,您的字符串将形成如下 -

\"

因此,对于输入字符串this \"is a test" wawawawa this \\"is a test" wawawawa this \\\"is a test" wawawawa ,每次"this \"is a test\" wawawawa"找到strchr(string, flag) "的最后插入\"时,您的while循环将无限次运行。

第二个问题是你在每次迭代中在while循环中进行的内存分配。分配给free()的内存没有new。因此,当while循环无限运行时,它将占用导致 - the PC collapses的所有内存。

要解决此问题,在每次迭代中,只应在从最后一次插入flag后的字符开始到字符串末尾的字符串中搜索substring。另外,请确保free()动态分配的内存。

答案 3 :(得分:1)

一些建议:

  • 避免typedef char* str;char *类型在C中很常见,屏蔽它只会让您的代码更难被审核
  • 出于同样的原因,不要#define salloc(size) (str)malloc(size+1)。此外不要在C
  • 中投放malloc
  • 每次撰写malloc(或callocrealloc)时,都应该有相应的free:C没有垃圾回收
  • 动态分配很昂贵,仅在需要时使用。换句话说,循环中的malloc应该被查看两次(特别是如果没有相应的free
  • 总是测试分配函数(不相关:和io)当你耗尽内存时,malloc将只返回NULL。一个很好的错误消息比崩溃更容易理解
  • 学会使用调试器:如果你在调试器下执行了代码,那么错误很明显

接下来的原因:如果替换字符串包含原始字符串,则会再次出现并在无限循环中运行

一种可能的解决方法:在循环之前分配结果字符串,并在原始字符串和结果中前进。它将为您节省不必要的分配和取消分配,并且不受替换字符串中存在的原始字符的影响。

可能的代码:

// the result is an allocated string that must be freed by caller
str strcinsert(str string, char flag, str substring)
{
    int nflag = 0; //this is the number of times the character appears
    for (int i = 0; i<strlen(string); i++)
        if (string[i] == flag)
            nflag++;
    str new_ = string;
    int pos;
    new_ = salloc(strlen(string) + nflag*strlen(substring) - nflag);
    // should test new_ != NULL
    char * cur = new_;
    char *old = string;
    while (NULL != (string = strchr(string, flag))) //since when its not found returns NULL
    {
        pos = string - old;
        strncpy(cur, old, pos);
        cur[pos] = '\0';             // strncpy does not null terminate the dest. string
        strcat(cur, substring);
        strcat(cur, string + 1);
        cur += strlen(substring) + pos; // advance the result
        old = ++string;                 // and the input string
    }
    return new_;
}

注意:我还没有恢复strsalloc,但您真的应该这样做。