在C中将字符串插入另一个字符串
Inserting strings into another string in C
我正在实现一个函数,给定一个字符串、一个字符和另一个字符串(因为现在我们可以称它为 "substring");将子字符串放在字符串中字符所在的任何位置。
为了更好地解释我,给定这些参数,这是函数应该 return (伪代码):
func ("aeiou", 'i', "hello") -> aehelloou
我正在使用 string.h
库中的一些函数。我已经测试过,结果很好:
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;
}
感谢您的帮助!
在你的第二个循环中,你总是寻找字符串中的第一个 flag
字符。在这种情况下,这将是您刚刚从 substring
插入的那个。 strchr
函数将始终找到该引号,而永远不会找到 return NULL
,因此您的循环将永远不会终止,只会继续分配内存(而且内存不够,因为您的字符串会任意变大) .
说到分配内存,你需要更加小心。与 Python 不同,当您不再使用内存时,C 不会自动注意到;任何你 malloc
必须 free
d。您还分配了比您需要的更多的内存:即使在您的工作 "this$ is a tes$t wawawa$wa"
示例中,您为循环的 每次迭代 上的完整字符串分配了足够的 space,永远不会 free
任何一个。你应该 运行 在第二次循环之前分配一次。
这不像上面的东西那么重要,但你也应该注意性能。每次调用 strcat
和 strlen
都会遍历整个字符串,这意味着您查看它的次数远远超过您需要的次数。您应该改为保存 strlen
的结果,并将新字符串直接复制到您知道 NUL 终止符所在的位置。 strchr
也是如此;您已经替换了字符串的开头并且不想浪费时间再次查看它,除了导致当前错误的部分。
与这些问题相比,您的typedef和macro的评论中提到的样式问题相对较小,但仍然值得一提。 C 中的 char*
与 Python 中的 str
不同;尝试将其 typedef
命名为相同的名称只会让您更有可能尝试将它们视为相同的并且 运行 进入这些问题。
I don't know why but it stops working
strchr(string, flag)
正在查看整个字符串以查找标志。搜索需要限制在字符串中尚未 examined/updated 的部分。通过重新搜索部分替换的字符串,代码一遍又一遍地找到 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 + [=10=]
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 = '[=10=]';
}
return dest;
}
在您的程序中,您遇到了输入问题 -
char *somestring= "this \"is a test\" wawawawa";
因为你想用 "
替换 \"
。
第一个问题是,每当您将 "
替换为 string
中的 \"
时,在下一次迭代中 strchr(string, flag)
将找到最后插入的 "
\"
。因此,在随后的交互中,您的字符串将像这样形成 -
this \"is a test" wawawawa
this \"is a test" wawawawa
this \\"is a test" wawawawa
因此,对于输入字符串 "this \"is a test\" wawawawa"
,您的 while 循环将 运行 无限次,因为每次 strchr(string, flag)
找到最后插入的 "
of \"
.
第二个问题是您在每次迭代的 while
循环中进行的内存分配。分配给new
的内存没有free()
。因此,当 while
无限循环 运行 时,它将耗尽所有内存,从而导致 - the PC collapses
.
要解决这个问题,在每次迭代中,您应该只在从最后插入的 substring
之后的字符开始到字符串末尾的字符串中搜索 flag
。另外,确保 free()
动态分配的内存。
一些建议:
- 避免
typedef char* str;
。 char *
类型在 C 语言中很常见,屏蔽它只会让你的代码更难被审查
- 出于完全相同的原因避免
#define salloc(size) (str)malloc(size+1)
。另外不要在C 中投malloc
- 每次你写一个
malloc
(或calloc
或realloc
)应该有一个相应的free
:C没有垃圾收集
- 动态分配很昂贵,只在需要时使用它。换句话说,循环中的
malloc
应该被看两次(特别是如果没有相应的 free
)
- 始终测试分配函数(与 io 无关)当您耗尽内存时,malloc 将简单地 return 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] = '[=10=]'; // 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_;
}
注意:我没有还原 str
和 salloc
但你确实应该还原。
我正在实现一个函数,给定一个字符串、一个字符和另一个字符串(因为现在我们可以称它为 "substring");将子字符串放在字符串中字符所在的任何位置。 为了更好地解释我,给定这些参数,这是函数应该 return (伪代码):
func ("aeiou", 'i', "hello") -> aehelloou
我正在使用 string.h
库中的一些函数。我已经测试过,结果很好:
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;
}
感谢您的帮助!
在你的第二个循环中,你总是寻找字符串中的第一个 flag
字符。在这种情况下,这将是您刚刚从 substring
插入的那个。 strchr
函数将始终找到该引号,而永远不会找到 return NULL
,因此您的循环将永远不会终止,只会继续分配内存(而且内存不够,因为您的字符串会任意变大) .
说到分配内存,你需要更加小心。与 Python 不同,当您不再使用内存时,C 不会自动注意到;任何你 malloc
必须 free
d。您还分配了比您需要的更多的内存:即使在您的工作 "this$ is a tes$t wawawa$wa"
示例中,您为循环的 每次迭代 上的完整字符串分配了足够的 space,永远不会 free
任何一个。你应该 运行 在第二次循环之前分配一次。
这不像上面的东西那么重要,但你也应该注意性能。每次调用 strcat
和 strlen
都会遍历整个字符串,这意味着您查看它的次数远远超过您需要的次数。您应该改为保存 strlen
的结果,并将新字符串直接复制到您知道 NUL 终止符所在的位置。 strchr
也是如此;您已经替换了字符串的开头并且不想浪费时间再次查看它,除了导致当前错误的部分。
与这些问题相比,您的typedef和macro的评论中提到的样式问题相对较小,但仍然值得一提。 C 中的 char*
与 Python 中的 str
不同;尝试将其 typedef
命名为相同的名称只会让您更有可能尝试将它们视为相同的并且 运行 进入这些问题。
I don't know why but it stops working
strchr(string, flag)
正在查看整个字符串以查找标志。搜索需要限制在字符串中尚未 examined/updated 的部分。通过重新搜索部分替换的字符串,代码一遍又一遍地找到 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 + [=10=]
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 = '[=10=]';
}
return dest;
}
在您的程序中,您遇到了输入问题 -
char *somestring= "this \"is a test\" wawawawa";
因为你想用 "
替换 \"
。
第一个问题是,每当您将 "
替换为 string
中的 \"
时,在下一次迭代中 strchr(string, flag)
将找到最后插入的 "
\"
。因此,在随后的交互中,您的字符串将像这样形成 -
this \"is a test" wawawawa
this \"is a test" wawawawa
this \\"is a test" wawawawa
因此,对于输入字符串 "this \"is a test\" wawawawa"
,您的 while 循环将 运行 无限次,因为每次 strchr(string, flag)
找到最后插入的 "
of \"
.
第二个问题是您在每次迭代的 while
循环中进行的内存分配。分配给new
的内存没有free()
。因此,当 while
无限循环 运行 时,它将耗尽所有内存,从而导致 - the PC collapses
.
要解决这个问题,在每次迭代中,您应该只在从最后插入的 substring
之后的字符开始到字符串末尾的字符串中搜索 flag
。另外,确保 free()
动态分配的内存。
一些建议:
- 避免
typedef char* str;
。char *
类型在 C 语言中很常见,屏蔽它只会让你的代码更难被审查 - 出于完全相同的原因避免
#define salloc(size) (str)malloc(size+1)
。另外不要在C 中投 - 每次你写一个
malloc
(或calloc
或realloc
)应该有一个相应的free
:C没有垃圾收集 - 动态分配很昂贵,只在需要时使用它。换句话说,循环中的
malloc
应该被看两次(特别是如果没有相应的free
) - 始终测试分配函数(与 io 无关)当您耗尽内存时,malloc 将简单地 return NULL。一个很好的错误消息比崩溃更容易理解
- 学习使用调试器:如果您在调试器下执行代码,错误就会很明显
malloc
下一个原因:如果替换字符串包含原始字符串,您将再次落在它上面并且运行陷入无限循环
一种可能的解决方法:在循环之前分配结果字符串,并在原始字符串和结果中都前进。它将避免不必要的分配和取消分配,并且不受替换字符串中存在的原始字符的影响。
可能的代码:
// 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] = '[=10=]'; // 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_;
}
注意:我没有还原 str
和 salloc
但你确实应该还原。