查找和替换特定文本的功能?

时间:2018-09-11 00:31:21

标签: c string char substitution

有没有可以使用的功能,可以代替特定的文本。

例如: char *test = "^Hello world^";将替换为char *test = "<s>Hello world</s>";

另一个示例:char *test2 = "This is ~my house~ bud"将替换为char *test2 = "This is <b>my house</b> bud"

1 个答案:

答案 0 :(得分:1)

在开始替换字符串中的子字符串之前,您必须了解要处理的内容。在您的示例中,您想知道是否可以替换字符串中的字符,并举一个示例:

char *test = "^Hello world^";

通过test进行声明和初始化,它是字符串字面,它是在只读内存(实际上在所有系统上)和任何尝试修改只读存储器中存储的字符会调用未定义行为(很可能是分段错误)

如评论中所述,test可以声明并初始化为字符数组,例如char test[] = "^Hello world^";并确保test是可修改的,但这不能解决替换字符串比被替换子字符串长的问题。

要处理其他字符,您有两个选择(1)您可以声明test[]足够大以容纳替换,或者(2)您可以为替换字符串动态分配存储,而{{ 1}}如果您达到了原始分配限制,则会增加内存。

例如,如果您将与realloc关联的代码限制为一个函数,则可以声明test并使用足够数量的字符来处理替换字符,例如

test

然后,您只需要跟踪原始字符串的长度以及每次替换后添加的字符数,并确保总数不超过#define MAXC 1024 /* define a constant for the maximum number of characters */ ... test[MAXC] = "^Hello world^"; (为 nul终止保留空间字符)。

但是,如果您决定将替换代码移到一个单独的函数中,则会遇到无法返回指向本地声明的数组的指针的问题(因为该本地声明的数组是在函数堆栈空间中声明的,函数返回时销毁(释放以供重用))本地声明的数组具有自动存储持续时间。参见:C11 Standard - 6.2.4 Storage durations of objects

为避免本地声明的数组无法在函数返回中幸存的问题,您可以简单地为新字符串动态分配存储空间,这会导致新字符串具有已分配存储期限,这对于程序的寿命,或者直到通过调用MAXC-1释放内存为止。这样,您就可以为函数中的新字符串声明和分配存储,替换子字符串,然后返回指向新字符串的指针,以供在调用函数中使用。

对于您而言,在函数中简单声明新字符串并分配两倍于原始字符串的存储量是一种合理的方法。 (您仍然必须跟踪所使用的内存字节数,但是如果您应该达到原始分配限制,则可以free()附加内存)此过程可以继续并容纳任意数量的字符串和替换,直到系统上的可用内存。

虽然有多种方法可以进行替换,只需在原始字符串中搜索每个子字符串,然后将文本复制到新的子字符串中,然后复制替换子字符串即可, ”,从原始字符串的开头到结尾进行替换。您面临的唯一挑战是跟踪使用的字符数(以便可以在必要时进行重新分配),并在开始时从头到尾提高阅读位置。

您的示例在处理字符串的过程中需要在两个替换字符串之一之间交替,这使该过程有些复杂。这可以通过简单的切换标志来处理。 (由变量realloc替代),该变量将确定适当的替换字符串以在需要的地方使用。

三元运算符(例如0,1,0,1,...可以帮助减少代码中散布的test ? if_true : if_false;块的数量-由您自己决定。如果{{1 }}格式对您来说更易读-使用该格式,否则请使用三元

以下示例将(1)原始字符串,(2)查找子字符串,(3)第一个替换子字符串和(4)第二个替换子字符串作为程序的参数。它在if (test) { if_true; } else { if_false; }函数中分配新字符串,进行替换请求,并将指向新字符串的指针返回到调用函数。对该代码进行了严重注释,以帮助您进行后续操作,例如

if (test) {}

(上面的strreplace()函数使用指针向下移动(“寸蠕虫”)进行替换的原始字符串,但是如果您觉得更有意义,则可以使用字符串索引和索引变量)

还要注意,将#include <stdio.h> #include <stdlib.h> #include <string.h> /* replace all instances of 'find' in 's' with 'r1' and `r2`, alternating. * allocate memory, as required, to hold string with replacements, * returns allocated string with replacements on success, NULL otherwise. */ char *strreplace (const char *s, const char *find, const char *r1, const char *r2) { const char *p = s, /* pointer to s */ *sp = s; /* 2nd substring pointer */ char *newstr = NULL, /* newsting pointer to allocate/return */ *np = newstr; /* pointer to newstring to fill */ size_t newlen = 0, /* length for newstr */ used = 0, /* amount of allocated space used */ slen = strlen (s), /* length of s */ findlen = strlen (find), /* length of find string */ r1len = strlen (r1), /* length of replace string 1 */ r2len = strlen (r2); /* length of replace string 2 */ int toggle = 0; /* simple 0/1 toggle flag for r1/r2 */ if (s == NULL || *s == 0) { /* validate s not NULL or empty */ fputs ("strreplace() error: input NULL or empty\n", stderr); return NULL; } newlen = slen * 2; /* double length of s for newstr */ newstr = calloc (1, newlen); /* allocate twice length of s */ if (newstr == NULL) { /* validate ALL memory allocations */ perror ("calloc-newstr"); return NULL; } np = newstr; /* initialize newpointer to newstr */ /* locate each substring using strstr */ while ((sp = strstr (p, find))) { /* find beginning of each substring */ size_t len = sp - p; /* length to substring */ /* check if realloc needed? */ if (used + len + (toggle ? r2len : r1len) + 1 > newlen) { void *tmp = realloc (newstr, newlen * 2); /* realloc to temp */ if (!tmp) { /* validate realloc succeeded */ perror ("realloc-newstr"); return NULL; } newstr = tmp; /* assign realloc'ed block to newstr */ newlen *= 2; /* update newlen */ } strncpy (np, p, len); /* copy from pointer to substring */ np += len; /* advance newstr pointer by len */ *np = 0; /* nul-terminate (already done by calloc) */ strcpy (np, toggle ? r2 : r1); /* copy r2/r1 string to end */ np += toggle ? r2len : r1len; /* advance newstr pointer by r12len */ *np = 0; /* <ditto> */ p += len + findlen; /* advance p by len + findlen */ used += len + (toggle ? r2len : r1len); /* update used characters */ toggle = toggle ? 0 : 1; /* toggle 0,1,0,1,... */ } /* handle segment of s after last find substring */ slen = strlen (p); /* get remaining length */ if (slen) { /* if not at end */ if (used + slen + 1 > newlen) { /* check if realloc needed? */ void *tmp = realloc (newstr, used + slen + 1); /* realloc */ if (!tmp) { /* validate */ perror ("realloc-newstr"); return NULL; } newstr = tmp; /* assign */ newlen += slen + 1; /* update (not required here, know why? */ } strcpy (np, p); /* add final segment to string */ *(np + slen) = 0; /* nul-terminate */ } return newstr; /* return newstr */ } int main (int argc, char **argv) { const char *s = NULL, *find = NULL, *r1 = NULL, *r2 = NULL; char *newstr = NULL; if (argc < 5) { /* validate required no. or arguments given */ fprintf (stderr, "error: insufficient arguments,\n" "usage: %s <find> <rep1> <rep2>\n", argv[0]); return 1; } s = argv[1]; /* assign arguments to poitners */ find = argv[2]; r1 = argv[3]; r2 = argv[4]; newstr = strreplace (s, find, r1, r2); /* replace substrings in s */ if (newstr) { /* validate return */ printf ("oldstr: %s\nnewstr: %s\n", s, newstr); free (newstr); /* don't forget to free what you allocate */ } else { /* handle error */ fputs ("strreplace() returned NULL\n", stderr); return 1; } return 0; } 用于原始分配。strreplace会将新的内存分配并设置为全零,这有助于确保您不会忘记来使字符串 nul-terminate ,但是请注意,calloc添加的任何内存都不会归零-除非您使用calloc等手动将其归零。在每次复制后终止新字符串,因此您可以使用reallocmemset进行分配)

使用/输出示例

第一个示例:

malloc

第二个示例:

calloc

内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于任何分配的内存块,您都有2个职责:(1)始终保留指向起始地址的指针因此,(2)当不再需要它时可以释放

当务之急是使用一个内存错误检查程序来确保您不会尝试访问内存或在已分配的块的边界之外/之外进行写入,不要试图以未初始化的值读取或基于条件跳转,最后,以确认您释放了已分配的所有内存。

对于Linux,$ ./bin/str_substr_replace2 "^Hello world^" "^" "<s>" "</s>" oldstr: ^Hello world^ newstr: <s>Hello world</s> 是正常选择。每个平台都有类似的内存检查器。它们都很容易使用,只需通过它运行程序即可。

$ ./bin/str_substr_replace2 "This is ~my house~ bud" "~" "<b>" "</b>"
oldstr: This is ~my house~ bud
newstr: This is <b>my house</b> bud

始终确认已释放已分配的所有内存,并且没有内存错误。

仔细检查一下,如果还有其他问题,请告诉我。