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

问题描述 投票:1回答:1

是否有一个我可以使用的功能,这将允许我替换特定的文本。

例如: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"取代

c string char substitution
1个回答
1
投票

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

char *test = "^Hello world^";

通过如上所示声明和初始化test,是在只读存储器中创建的字符串文字(几乎在所有系统上),任何修改存储在只读存储器中的字符的尝试都会调用未定义的行为(很可能是分段错误)

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

要处理其他字符,您有两个选项(1)您可以声明test[]足够大以容纳替换,或者(2)您可以为替换字符串动态分配存储空间,并且如果达到原始分配则为realloc额外内存限制。

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

#define MAXC 1024  /* define a constant for the maximum number of characters */
...
    test[MAXC] = "^Hello world^";

然后,您只需要跟踪原始字符串长度加上每次替换时添加的字符数,并确保总数不会超过MAXC-1(为nul-terminating字符保留空间)。

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

为了避免本地声明的数组无法在函数返回中存活的问题,您可以简单地为新字符串动态分配存储空间,这会导致新字符串具有分配的存储持续时间,这有利于程序的生命周期,或直到内存为止。通过调用free()释放。这允许您为函数中的新字符串声明和分配存储空间,进行子字符串替换,然后返回指向新字符串的指针,以便在调用函数中使用。

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

虽然有很多种方法可以接近替换,只需在原始字符串中搜索每个子字符串,然后将文本复制到子字符串到新字符串,然后复制替换子字符串就可以从“字符串蠕虫”中获取开始到原始字符串的结尾,随时进行替换替换。您唯一的挑战是跟踪所使用的字符数(这样您可以在必要时重新分配)并在您开始时将您在原始位置的读取位置从开始到结束。

您的示例在处理字符串时需要在两个替换字符串之一之间切换时,会使某个过程变得复杂。这可以通过简单的切换标志来处理。 (替换0,1,0,1,...的变量),然后确定在需要的地方使用的正确替换字符串。

三元运算符(例如test ? if_true : if_false;可以帮助减少你在代码中散布的if (test) { if_true; } else { if_false; }块的数量 - 这取决于你。如果if (test) {}格式对你更具可读性 - 使用它,否则,使用三元组。

以下示例采用(1)原始字符串,(2)查找子字符串,(3)第一个替换子字符串,以及(4)第二个替换子字符串作为程序的参数。它为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函数使用指针行走(“inch-worm”)沿着原始字符串进行替换,但如果对你更有意义,你可以使用字符串索引和索引变量)

(还要注意使用calloc进行原始分配.calloc分配并设置新内存为零,这有助于确保你不会忘记nul-terminate你的字符串,但请注意realloc添加的任何内存都不会归零 - 除非你用memset等手动将它归零。上面的代码在每个副本之后手动终止新字符串,所以你可以使用malloccalloc进行分配)

示例使用/输出

第一个例子:

$ ./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

内存使用/错误检查

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

您必须使用内存错误检查程序,以确保您不会尝试访问内存或写入超出/超出已分配块的范围,尝试读取或基于未初始化值的条件跳转,最后,确认你释放了你分配的所有内存。

对于Linux,valgrind是正常的选择。每个平台都有类似的记忆检查器。它们都很简单易用,只需通过它运行程序即可。

$ valgrind ./bin/str_substr_replace2 "This is ~my house~ bud" "~" "<b>" "</b>"
==8962== Memcheck, a memory error detector
==8962== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==8962== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==8962== Command: ./bin/str_substr_replace2 This\ is\ ~my\ house~\ bud ~ \<b\> \</b\>
==8962==
oldstr: This is ~my house~ bud
newstr: This is <b>my house</b> bud
==8962==
==8962== HEAP SUMMARY:
==8962==     in use at exit: 0 bytes in 0 blocks
==8962==   total heap usage: 1 allocs, 1 frees, 44 bytes allocated
==8962==
==8962== All heap blocks were freed -- no leaks are possible
==8962==
==8962== For counts of detected and suppressed errors, rerun with: -v
==8962== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

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

仔细看看,如果您有任何其他问题,请告诉我。

© www.soinside.com 2019 - 2024. All rights reserved.