根据这个问题here中的建议,我编写了以下程序来查找并替换字符串中的目标子字符串。 (它可以在循环中运行,以使用strstr()
字符串方法查找和替换“target”的所有实例,但我在这里举一个示例。)
#include <stdio.h>
#include <string.h>
#define SUCCESS 1
#define FAILURE 0
#define STR_CAP 80 + 1
int stringify(char *string, const char *target, const char *replacement) {
char segment[STR_CAP];
int S = strlen(string), T = strlen(target);
char pre_segment[STR_CAP];
char post_segment[STR_CAP];
for (int i = 0; i < S; i++) {
strncpy(segment, string + i, T);
segment[T] = '\0';
if (strcmp(segment, target) == 0) {
memcpy(pre_segment, string, i);
/*printf("pre_segment: %s\n", pre_segment);*/
pre_segment[i] = '\0';
memcpy(post_segment, string + i + T, S - (i + T));
post_segment[S - (i + T)] = '\0'; /*WHAT IS THIS MAGIC?*/
/*printf("post_segment: %s\n", post_segment);*/
strcat(pre_segment, replacement);
/*printf("pre_segment after concat.: %s\n", pre_segment);*/
strcat(pre_segment, post_segment);
/*printf("pre_segment after concat,: %s\n", pre_segment);*/
strcpy(string, pre_segment);
return SUCCESS;
}
} return FAILURE;
}
int main() {
char string[] = "The quick brown fox jumped over the lazy, brown dog.";
char target[] = "brown";
char replacement[] = "ochre-ish";
stringify(string, target, replacement);
printf("%s\n", string);
stringify(string, target, replacement);
printf("%s\n", string);
return 0;
}
但有一些我不明白的事情。
(1)一方面:在我使用memcpy()
并手动设置我复制到'\0'
的字符串的末尾之前,我一直得到缓冲区溢出(以堆栈粉碎的形式) ,以及奇怪的错误。这有什么影响,只是天真地使用strncpy()
不?例如,如果您注释掉“MAGIC”行,那么程序就会崩溃。
(2)这样做有更好或更规范的方法吗?这感觉非常笨重,但我找不到任何关于子串替换的基本“how-tos”。
答案 0 :(得分:1)
memcpy()
仅复制内存,并且没有字符串和空终止符的重新收集。 strncpy()
将复制前n个字符,并且只有在n个字符中找到空终止符时才会用0(空终止符)填充其余字符,所以你有可能给strncpy()
字符串的长度{}想要复制,它做了,但没有找到一个空终止符。从这个意义上说,strncpy()
就像memcpy()
一样。无论哪种方式,您都需要手动分配空终止符。
内存被损坏只是因为程序读取了一个字符串,但没有找到空终止符,因此将继续读取,直到它经过堆栈,堆和内存的其余部分,直到它到达空终止符(不是那个顺序)。
有很多方法可以进行子串替换,这是另一种方法,它更优雅:What is the function to replace string in C?
编辑回应:“你能解释一下memcpy()的作用以及人们对strncpy()如此不满意的原因吗?”
想象一下你的记忆如下:
0 3
01 | 30 | 40 | 00 | 32 | 40
然后执行memcpy(0x03, 0x00, 3);
告诉计算机将3个字节从地址0复制到地址3.现在我的内存看起来像这样:
0 3
01 | 30 | 40 | 01 | 30 | 40
计算机完全按照我的指示逐字节复制。让我们来看看strncpy()
在这种情况下会做什么。从上面的第一个图开始,调用strncpy(0x03, 0x00, 3);
告诉计算机将空终止的char数组(字符串)复制到0x00,然后复制到3个字符。结果是:
0 3
01 | 30 | 40 | 01 | 30 | 40
让我们想象一下,我们开始时:
0 3
65 | 00 | 40 | 01 | 30 | 40
这里,地址0x00实际上是指1个字符长的字符串:“A”。
现在,如果我们调用相同的strncpy(0x03, 0x00, 3)
计算机首先复制65,请注意空终止符,然后停在那里。然后用0填充其余部分,结果如下:
0 3
65 | 00 | 40 | 65 | 00 | 00
您可以看到,如果您的字符串超过3个字符,strncpy
将不会为您复制空终结符,因为它将“最大化”。这与memcpy
相同。如果在前n个字节中遇到空终止符,strncpy
将仅将空终止符移动到目标。因此,您可以在前几个示例中看到,即使地址0x00包含3个字符的有效字符串(在地址0x03处以0结尾),strncpy()
函数也未在目标(0x03)处生成有效字符串,因为0x04可以是任何东西(不一定是空终止符)。
答案 1 :(得分:1)
memcpy()
只是将一个字节从一个数组复制到另一个数组。这是一个非常简单的功能,有两个注意事项:
char
类型根据定义恰好是一个字节,因此大小只是字符数,如果需要,还为空终止符加1。 memmove()
与memcpy
的作用相同,但它可用于重叠对象。它需要额外的测试来正确处理所有情况。
使用这些函数,这是stringify
函数的更简单版本:
#include <stdio.h>
#include <string.h>
int stringify(char *dest, const char *target, const char *replace) {
char *p = strstr(dest, target);
if (p == NULL) {
/* no replacement */
return 0;
}
size_t len1 = strlen(target);
size_t len2 = strlen(replace);
if (len1 != len2) {
/* move the remainder of the string into the right place */
memmove(p + len2, p + len1, strlen(p + len1) + 1);
}
memcpy(p, replace, len2);
return 1;
}
int main(void) {
char string[80] = "The quick brown fox jumped over the lazy, brown dog.";
char target[] = "brown";
char replacement[] = "ochre-ish";
stringify(string, target, replacement);
printf("%s\n", string);
stringify(string, target, replacement);
printf("%s\n", string);
return 0;
}
注意:
memmove
代替memcpy
来移动字符串的其余部分,因为源和目标数组重叠。stringify
可能无法产生预期的替换:将brown
替换为brownish
显然会因多次匹配而失败。替换循环中的所有匹配项,并分配生成的字符串并不困难:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *replace_all(const char *str, const char *target, const char *replace) {
size_t len1 = strlen(target);
size_t len2 = strlen(replace);
size_t matches = 0;
const char *p, *p0;
char *dest, *q;
if (len1 > 0) {
for (p = str; p = strstr(p, target) != NULL; p += len1)
matches++;
}
if (matches == 0) {
return strdup(str);
}
dest = malloc(strlen(str) - len1 * matches + len2 * matches + 1);
if (dest != NULL) {
for (p = str, q = dest; (p = strstr(p0 = p, target)) != NULL; p += len1) {
memcpy(q, p0, p - p0);
q += p - p0;
memcpy(q, replace, len2);
q += len2;
}
memcpy(q, p0, strlen(p0) + 1);
}
return dest;
}
int main(void) {
char string[] = "The quick brown fox jumped over the lazy, brown dog.";
char target[] = "brown";
char replacement[] = "brownish";
char *p = replace_all(string, target, replacement);
printf("%s\n", p);
free(p);
return 0;
}
答案 2 :(得分:0)
您的方法存在一个大问题:replacement
可能足够长
结果字符串的长度超过80个字符。
我这样做
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *stringify(const char *src, const char *target, const char *replacement)
{
int buffer_len = 0;
char *buffer = NULL, *tmp;
if(src == NULL || replacement == NULL || target == NULL)
return NULL;
int repl_len = strlen(replacement);
int target_len = strlen(target);
while(tmp = strstr(src, target) && target_len)
{
// get length of string until target
int substr_len = tmp - src;
// get more space
char *new_buff = realloc(buffer, buffer_len + substr_len + repl_len + 1);
if(new_buff == NULL)
{
// out of memory
return buffer;
}
if(buffer == NULL) // first realloc
*new_buff = 0;
buffer = new_buff;
// copy that what is before the found target
strncat(buffer, src, substr_len);
buffer[buffer_len + substr_len] = 0;
//// copy the replacement at the end of buffer
strncat(buffer, replacement, repl_len);
buffer[buffer_len + substr_len + repl_len] = 0;
buffer_len += substr_len + repl_len;
//// set src to the next character after the replacement
src = tmp + target_len;
}
if(buffer == NULL) // target never found
{
buffer = malloc(strlen(src) + 1);
if(buffer == NULL)
return NULL;
strcpy(buffer, src);
} else {
if(*src) {
// copy the leftover
int leftover_len = strlen(src);
char *new_buff = realloc(buffer, buffer_len + leftover_len + 1);
if(new_buff == NULL)
return buffer; // could not expand more, return all we've converted
buffer = new_buff;
strcat(buffer, src);
}
}
return buffer;
}
int main(void)
{
char string[] = "The quick brown fox jumped over the lazy, brown dog.";
char target[] = "brown";
char replacement[] = "ochre-ish";
char *new_txt = stringify(string, target, replacement);
if(new_txt)
{
printf("new string: %s\n", new_txt);
free(new_txt);
} else
printf("Out of memory\n");
return 0;
}
当然你可以改进代码,但我的代码应该显示如何使用
realloc
和指针算术在你开始时非常重要
操纵字符串或数据数组。