通过删除字符而不创建新字符串来编辑字符串,这是否合法?
edit a string by removing characters without creating a new string, is it legit or not?
编辑:我想我已经理解这个概念是如何工作的,这是我的代码
void delete_duplicate(char* str) {
if (str == NULL) {
exit(1);
}
for (size_t i = 0; str[i] != 0; i++) {
if (str[i] == str[i + 1]) {
str[i] = '[=10=]';
}
}
}
int main(void) {
char str[] = "hhhhhhhheeeeeeeeyyyyyyyyyyy";
delete_duplicate(str);
return 0;
}
输出字符串是“00...0h0...000e0...000y”(有很多零)。如果字符串是“abbbb”,则字符串变为“a”而不是“ab”。
我在考虑这个练习的算法:
given a string, for example "ssttringg" the output must be "string". the function has to remove one character if and only if the current character and the next character are the same.
该练习需要一个像这样声明的函数:
extern void delete_consecutive(char* str);
这是我的算法:
- 循环:如果当前字符等于下一个字符,长度加1,重复直到到达零终止符。
关键部分:动态内存分配。
- 分配足够的内存来存储输出。为什么我说这是关键部分?因为我必须在一个 void 函数中执行它,所以我不能创建第二个字符串,用 malloc 分配足够的内存,然后在最后 return 它。我需要改为编辑原始字符串。我想我可以通过重新分配并减少字符串的大小来做到这一点。但我不知道该怎么做,更重要的是:我不知道在不丢失数据的情况下这样做是否合法。我想我把作业复杂化了,但它是一个空函数,因此我不能简单地创建一个新字符串并在其中复制字符,因为我不能 return 它。
如果不清楚,我会编辑问题。
如果作为参数传递的字符串是可修改的,您可以修改它。
示例:
void deleteFirstChar(char *str)
{
if(str && *str)
{
memmove(str, str + 1, strlen(str));
}
}
//illegal call
//string literals cannot be changed
void foo(void)
{
char *str = "Hello";
deleteFirstChar(str);
}
//legal call
void bar(void)
{
char str[] = "Hello";
deleteFirstChar(str);
}
这不是答案,但请考虑以下代码:
char str[] = "strunqg";
printf("before: %s\n", str);
modifystring(str);
printf("after: %s\n", str);
其中“modifystring
”函数如下所示:
void modifystring(char *p)
{
p[3] = 'i';
p[5] = p[6];
p[6] = '[=11=]';
}
这完全是“合法的”。但是,调用
是行不通的
char *str = "strunqg";
modifystring(str); /* WRONG */
或
modifystring("strunqg"); /* WRONG */
后两个中的任何一个都会尝试修改字符串文字,而这并不复杂。
void delete_duplicate(char* str)
{
if(str && *str)
{
char *writeTo = str, *readFrom = str;
char prev = 0;
while(*readFrom)
{
if(prev != *readFrom)
{
prev = *readFrom++;
*writeTo++ = prev;
}
else
{
readFrom++;
}
}
*writeTo = 0;
}
}
int main(void)
{
char str[] = "hhhhhhhheeeeeeeeyyyyyyyyyyy";
delete_duplicate(str);
printf("`%s`\n", str);
return 0;
}
此模式称为 保留扫描(也称为 缩减扫描,具体取决于您的观点),并且是 非常 在根据某些条件指示丢弃字符同时保留其他字符的算法中很常见。通常情况会发生变化,有时甚至开始扫描的方法也会有所改变
在最简单的形式中,它看起来像这样,例如:一种用于丢弃除数字(数字)字符以外的所有字符的算法:
- 从 reader
r
和 writer w
指针开始。
- 使用
r
从头到尾遍历字符串。 r
指针将始终 每次迭代恰好递增一次。
- 对于每次迭代,检查
r
处的当前字符是否满足保留条件(在这种情况下,字符是数字字符吗?)。如果是,写在 w
并提前 w
一个槽位。
- 完成时。
w
将指向您的终止符所在的位置。
#include <ctype.h>
void delete_nondigits(char *s)
{
if (s && *s)
{
char *w = s;
for (char *r = s; *r; ++r)
{
if (isdigit((unsigned char)*r))
*w++ = *r;
}
*w = 0;
}
}
很简单。
现在,in-place consecutive-run 压缩的算法更复杂,但具有高度相似的模型。因为留存是基于 prior-value already-retained,所以您需要记住 last-kept 值。您 可以 只使用 *w
,但如果您将算法保存在单独的备忘录中 char
,该算法实际上更容易理解(并推进 w
)正如您即将看到的那样:
- 像以前一样从 reader
r
和 writer w
指针开始,但是从字符串的 second 槽开始,并且单个备忘录 char
变量 c
初始化为字符串的第一个字符。
- 使用
r
遍历字符串直到遇到终止。 r
指针将始终 每次迭代恰好递增一次。
- 对于每次迭代,检查
*r
处的当前字符是否与备注字符相同 c
如果是,则什么都不做并继续下一次迭代。
- 否则,如果
*r
处的字符与备注字符 c
不同,则将其保存为 c
的新值, 写入 *w
, 前进 w
一个槽位.
- 完成后,通过在
*w
处设置终止符来终止字符串。
该算法的唯一障碍是在开始时支持 zero-length 字符串,通过首先检查单个条件可以轻松规避该字符串。我离开实际实施它作为你的练习。 (提示:仅当 (s && *s)
为真时才执行上述操作)。
编辑:我想我已经理解这个概念是如何工作的,这是我的代码
void delete_duplicate(char* str) {
if (str == NULL) {
exit(1);
}
for (size_t i = 0; str[i] != 0; i++) {
if (str[i] == str[i + 1]) {
str[i] = '[=10=]';
}
}
}
int main(void) {
char str[] = "hhhhhhhheeeeeeeeyyyyyyyyyyy";
delete_duplicate(str);
return 0;
}
输出字符串是“00...0h0...000e0...000y”(有很多零)。如果字符串是“abbbb”,则字符串变为“a”而不是“ab”。
我在考虑这个练习的算法:
given a string, for example "ssttringg" the output must be "string". the function has to remove one character if and only if the current character and the next character are the same.
该练习需要一个像这样声明的函数:
extern void delete_consecutive(char* str);
这是我的算法:
- 循环:如果当前字符等于下一个字符,长度加1,重复直到到达零终止符。
关键部分:动态内存分配。
- 分配足够的内存来存储输出。为什么我说这是关键部分?因为我必须在一个 void 函数中执行它,所以我不能创建第二个字符串,用 malloc 分配足够的内存,然后在最后 return 它。我需要改为编辑原始字符串。我想我可以通过重新分配并减少字符串的大小来做到这一点。但我不知道该怎么做,更重要的是:我不知道在不丢失数据的情况下这样做是否合法。我想我把作业复杂化了,但它是一个空函数,因此我不能简单地创建一个新字符串并在其中复制字符,因为我不能 return 它。
如果不清楚,我会编辑问题。
如果作为参数传递的字符串是可修改的,您可以修改它。
示例:
void deleteFirstChar(char *str)
{
if(str && *str)
{
memmove(str, str + 1, strlen(str));
}
}
//illegal call
//string literals cannot be changed
void foo(void)
{
char *str = "Hello";
deleteFirstChar(str);
}
//legal call
void bar(void)
{
char str[] = "Hello";
deleteFirstChar(str);
}
这不是答案,但请考虑以下代码:
char str[] = "strunqg";
printf("before: %s\n", str);
modifystring(str);
printf("after: %s\n", str);
其中“modifystring
”函数如下所示:
void modifystring(char *p)
{
p[3] = 'i';
p[5] = p[6];
p[6] = '[=11=]';
}
这完全是“合法的”。但是,调用
是行不通的 char *str = "strunqg";
modifystring(str); /* WRONG */
或
modifystring("strunqg"); /* WRONG */
后两个中的任何一个都会尝试修改字符串文字,而这并不复杂。
void delete_duplicate(char* str)
{
if(str && *str)
{
char *writeTo = str, *readFrom = str;
char prev = 0;
while(*readFrom)
{
if(prev != *readFrom)
{
prev = *readFrom++;
*writeTo++ = prev;
}
else
{
readFrom++;
}
}
*writeTo = 0;
}
}
int main(void)
{
char str[] = "hhhhhhhheeeeeeeeyyyyyyyyyyy";
delete_duplicate(str);
printf("`%s`\n", str);
return 0;
}
此模式称为 保留扫描(也称为 缩减扫描,具体取决于您的观点),并且是 非常 在根据某些条件指示丢弃字符同时保留其他字符的算法中很常见。通常情况会发生变化,有时甚至开始扫描的方法也会有所改变
在最简单的形式中,它看起来像这样,例如:一种用于丢弃除数字(数字)字符以外的所有字符的算法:
- 从 reader
r
和 writerw
指针开始。 - 使用
r
从头到尾遍历字符串。r
指针将始终 每次迭代恰好递增一次。 - 对于每次迭代,检查
r
处的当前字符是否满足保留条件(在这种情况下,字符是数字字符吗?)。如果是,写在w
并提前w
一个槽位。 - 完成时。
w
将指向您的终止符所在的位置。
#include <ctype.h>
void delete_nondigits(char *s)
{
if (s && *s)
{
char *w = s;
for (char *r = s; *r; ++r)
{
if (isdigit((unsigned char)*r))
*w++ = *r;
}
*w = 0;
}
}
很简单。
现在,in-place consecutive-run 压缩的算法更复杂,但具有高度相似的模型。因为留存是基于 prior-value already-retained,所以您需要记住 last-kept 值。您 可以 只使用 *w
,但如果您将算法保存在单独的备忘录中 char
,该算法实际上更容易理解(并推进 w
)正如您即将看到的那样:
- 像以前一样从 reader
r
和 writerw
指针开始,但是从字符串的 second 槽开始,并且单个备忘录char
变量c
初始化为字符串的第一个字符。 - 使用
r
遍历字符串直到遇到终止。r
指针将始终 每次迭代恰好递增一次。 - 对于每次迭代,检查
*r
处的当前字符是否与备注字符相同c
如果是,则什么都不做并继续下一次迭代。 - 否则,如果
*r
处的字符与备注字符c
不同,则将其保存为c
的新值, 写入*w
, 前进w
一个槽位. - 完成后,通过在
*w
处设置终止符来终止字符串。
该算法的唯一障碍是在开始时支持 zero-length 字符串,通过首先检查单个条件可以轻松规避该字符串。我离开实际实施它作为你的练习。 (提示:仅当 (s && *s)
为真时才执行上述操作)。