尝试替换文件中的文本会导致错误

时间:2018-02-08 23:19:40

标签: c

脚本的要点是采用三个参数。查找,替换,前缀。找到要替换的文本,替换为替换文本的内容,并且前缀是一种特殊情况。如果前缀在文本中,则用前缀+ replace替换前缀(某些文本)。我想知道为什么下面的代码在打开文件后立即抛出错误。如果被替换的文本被重复,例如" aaa"," bbb"它似乎只会引发错误。在哪里" a"正在被取代的是什么。

 Opened file.txt
 *** Error in `./a.out': malloc(): memory corruption: 0x00005652fbc55980 ***

打印后偶尔也会出现段故障"尝试替换文件......"。我的系统上的C和GDB不流利,导致只丢失库错误,这与此无关。 这是代码:

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

char concat(const char *s1, const char *s2)
{
    char *result = calloc(strlen(s1)+strlen(s2)+1, 1);
    strcpy(result, s1);
    strcat(result, s2);
    printf("Prefix will be replaced with %s.\n", result);
    return result;
}

static int replaceString(char *buf, const char *find, const char *replace, const char *prefix)
{

    int olen, rlen;
    char *s, *d;
    char *tmpbuf;

    if (!buf || !*buf || !find || !*find || !replace)
        return 0;

    tmpbuf = calloc(strlen(buf) + 1, 1);

    if (tmpbuf == NULL)
        return 0;

    olen = strlen(find);
    rlen = strlen(replace);

    s = buf;
    d = tmpbuf;

    while (*s) {
        if (strncmp(s, find, olen) == 0) {
            strcpy(d, replace);
            s += olen;
            d += rlen;
        }
        else
        {
            *d++ = *s++;
        }
    }

    *d = '\0';

   if(strcmp(buf, tmpbuf) == 0)
   {
          free(tmpbuf);
          return 0;
   }
   else
   {
         strcpy(buf, tmpbuf);
         free(tmpbuf);
         printf("%s", buf);
         printf("Replaced!\n");
         return 1;
   }

}

void getAndReplace(char* filename, char* find, char* replace, char* prefix)
{

   long length;
   FILE* f = fopen (filename, "r");
   char* buffer = 0;

   if (f)
   {
      fseek (f, 0, SEEK_END);
      length = ftell (f);
      fseek (f, 0, SEEK_SET);
      buffer = calloc(length+1, 1); //If i use malloc here, any file other than the first has garbage added to it. Why?
      if (buffer)
      {
        fread(buffer, 1, length, f);
      }
      fclose(f);
   }

   if(buffer)// && strlen(buffer) > 1)
   {
      int result = replaceString(buffer, find, replace, prefix);

      if(result == 0)
      {
         printf("Trying to replace prefix.\n");
         replace = concat(prefix, replace);
         result = replaceString(buffer, prefix, replace, "");
      }
      else
      {
         printf("Successfully replaced %s with %s\n", find, replace);
      }

      if(result == 1)
      {
         FILE* fp = fopen(filename, "w+");
         if(fp)
         {
             printf("Opened %s\n", filename);
             fprintf(fp, buffer);
             fclose(fp);
             printf("File %s overwritten with changes.\n", filename);
         }
      }
      else
      {
          printf("Nothing to replace for %s\n", filename);
      }
   }
   else
   {
     printf("Empty file.");
   }
   if(buffer)
   {
      free(buffer);
   }
}

int main(int argc, char **argv)
{

    if(argc < 4)
    {
      printf("Not enough arguments given: ./hw3 <find> <replace> <prefix>\n");
      return 1;
    }

    struct dirent *de;

    DIR *dr = opendir(".");

    if (dr == NULL)
    {
        printf("Could not open current directory\n");
        return 0;
    }

    while ((de = readdir(dr)) != NULL)
    {
       if(strlen(de->d_name) > 4 && !strcmp(de->d_name + strlen(de->d_name) - 4, ".txt"))
       {
            printf("Trying to replace for file %s\n", de->d_name);
            getAndReplace(de->d_name, argv[1], argv[2], argv[3]);
       }
    }

    closedir(dr);
    return 0;
}

1 个答案:

答案 0 :(得分:1)

我希望你concat能力

char concat(const char *s1, const char *s2);

只是一个错字,你的意思是

char *concat(const char *s1, const char *s2);

否则该函数将返回一个指针,就好像它是char

使用valgrind可以提供更多详细信息,无论您在哪里阅读/写作都是不允许的 你在哪里泄露记忆。没有它,很难确切地指出 地点。我注意到的一件事是,取决于findreplace的长度, 你可能没有足够的tmpbuf内存来导致缓冲区 溢出。

我认为编写replaceString的最佳方法是制作它 分配它自己需要的内存,而不是提供一个缓冲区来写入。 因为您从用户那里获得findreplace,所以您不知道 生成的缓冲区需要多大。你可以计算出来 事先,但你不这样做。如果要将预先分配的缓冲区传递给 replaceString,我将其作为双指针传递,以便replaceString可以执行 需要时realloc就可以了。或者在函数中分配内存并返回一个 指向已分配内存的指针。

这将是我的版本:

char *replaceString(const char *haystack, const char *needle, const char *replace)
{
    if(haystack == NULL || needle == NULL || replace == NULL)
        return NULL;

    char *dest = NULL, *tmp;

    size_t needle_len = strlen(needle);
    size_t replace_len = strlen(replace);
    size_t curr_len = 0;

    while(*haystack)
    {
        char *found = strstr(haystack, needle);

        size_t copy_len1 = 0;
        size_t new_size = 0;
        size_t pre_found_len = 0;

        if(found == NULL)
        {
            copy_len1 = strlen(haystack) + 1;
            new_size = curr_len + copy_len1;
        } else {
            pre_found_len = found - haystack;
            copy_len1 = pre_found_len;
            new_size = curr_len + pre_found_len + replace_len + 1;
        }


        tmp = realloc(dest, new_size);
        if(tmp == NULL)
        {
            free(dest);
            return NULL;
        }

        dest = tmp;

        strncpy(dest + curr_len, haystack, copy_len1);

        if(found == NULL)
            return dest; // last replacement, copied to the end

        strncpy(dest + curr_len + pre_found_len, replace, replace_len + 1);
        curr_len += pre_found_len + replace_len;

        haystack += pre_found_len + needle_len;
    }

    return dest;
}

这个版本的想法与你的相似,但我的内存重新分配了 它去了。我将参数的名称更改为与。具有相同的名称 strstr函数基于我的文档:

  

man strstr

char *strstr(const char *haystack, const char *needle);

因为我要更新haystack以指出复制的字符,我 使用这个循环:

while(*haystack)
{
    ...
}

这意味着它会在到达'\0' - 终止字节时停止。

第一件事是使用strstr来查找匹配needle的子字符串。 根据是否找到子字符串,我计算需要多少字节 复制到子字符串,以及缓冲区的新大小。在那之后我 重新分配缓冲区的内存并将所有内容复制到子字符串, 然后附加替换,更新curr_len变量并更新 haystack指向经过子字符串的指针。

如果未找到子字符串,则不再需要替换。所以我们必须这样做 复制haystack指向的字符串并返回构造的字符串。该 目的地的新尺寸为curr_len + strlen(haystack) + 1+1 因为我希望strncpy函数也复制'\0' - 终止字节)。 它必须复制strlen(haystack) + 1个字节。在第一个strncpy之后, 函数返回dest

如果找到子字符串,那么我们必须将所有内容复制到子字符串, 附加替换并更新当前长度和haystack指针。 首先,我计算字符串直到找到的子字符串并将其保存 pre_found_len。目的地的新大小将是 curr_len + pre_found_len + replace_len + 1(当前长度+长度 字符串直到substring +替换的长度+ 1为 '\0' - 终止字节)。现在只有strncpy个副本pre_found_len 字节。然后它复制替换。

现在你可以这样称呼它:

int main(void)
{
    const char *orig = "Is this the real life? Is this just fantasy?";
    char *text = replaceString(orig, "a", "_A_");
    if(text)
    {
        puts(orig);
        puts(text);
    }

    free(text);
}

将输出:

Is this the real life? Is this just fantasy?
Is this the re_A_l life? Is this just f_A_nt_A_sy?

现在,您可以在getAndReplace中使用此功能来替换prefix

char *getAndReplace(char* filename, char* find, char* replace, char* prefix)
{
    ...

    char *rep1 = replaceString(buffer, find, replace);

    if(rep1 == NULL)
    {
        // error
        free(buffer);
        return NULL;
    }

    char *prefix_rep = malloc(strlen(replace) + strlen(prefix) + 1);
    if(prefix_rep == NULL)
    {
        // error
        free(buffer);
        free(rep1);
        return NULL;
    }

    sprintf(prefix_rep, "%s%s", replace, prefix);

    char *rep2 = replaceString(rep1, prefix, prefix_rep);

    if(rep2 == NULL)
    {
        // error
        free(buffer);
        free(rep1);
        free(prefix_rep);
        return NULL;
    }

    // rep2 has all the replacements
    ...


    // before leaving
    free(buffer);
    free(rep1);
    free(prefix_rep);

    // returning all replacements
    return rep2;
}

使用malloc&amp;不要忘记检查他们是否返回NULL并且不要 忘记在不需要时释放记忆。

相关问题