1

我无法为从位置 P 开始从字符串 S 中删除 N 个字符的函数编写可行的代码。你们将如何编写这样的函数?

void remove_substring(char *s, int p, int n) {

    int i;

    if(n == 0) {
        printf("%s", s);
    }

    for (i = 0; i < p - 1; i++) {
        printf("%c", s[i]);
    }

    for (i = strlen(s) - n; i < strlen(s); i++) {
        printf("%c", s[i]);
    }


}

例子:

s: "abcdefghi"
p: 4
n: 3

输出:

abcghi

但是对于像 n = 0 和 p = 1 这样的情况,它不起作用!非常感谢!

4

6 回答 6

4

一些人已经向您展示了如何做到这一点,但他们的大多数解决方案都是高度浓缩的,使用标准库函数或者根本不解释发生了什么。这是一个版本,不仅包括一些非常基本的错误检查,还包括对正在发生的事情的一些解释:

void remove_substr(char *s, size_t p, size_t n)
{
  // p is 1-indexed for some reason... adjust it.
  p--;

  // ensure that we're not being asked to access
  // memory past the current end of the string.
  // Note that if p is already past the end of
  // string then p + n will, necessarily, also be
  // past the end of the string so this one check
  // is sufficient.
  if(p + n >= strlen(s))
    return;

  // Offset n to account for the data we will be
  // skipping.  
  n += p;

  // We copy one character at a time until we 
  // find the end-of-string character
  while(s[n] != 0)
    s[p++] = s[n++];

  // And make sure our string is properly terminated.  
  s[p] = 0;
}

需要注意的一个警告:请不要这样调用此函数:

remove_substr("abcdefghi", 4, 3);

或者像这样:

char *s = "abcdefghi";

remove_substr(s, 4, 3);

这样做会导致未定义的行为,因为字符串文字是只读的,并且标准不允许修改它们。

于 2013-08-21T15:25:18.163 回答
3

严格来说,您没有实现子字符串的删除:您的代码打印了删除了一系列字符的原始字符串。

另一件需要注意的是,根据您的示例,索引p是从 1 开始的,而不是像在 C 中那样从零开始。否则,输出 for"abcdefghi", 4, 3将是"abcdhi",而不是"abcghi"

考虑到这一点,让我们进行一些更改。首先,你的数学有点不对劲:最后一个循环应该是这样的:

for (i = p+n-1; i < strlen(s); i++) {
    printf("%c", s[i]);
}

ideone 上的演示。

如果您想使用 C 的从零开始的索引方案,请按如下方式更改循环:

for (i = 0; i < p; i++) {
    printf("%c", s[i]);
}
for (i = p+n; i < strlen(s); i++) {
    printf("%c", s[i]);
}

此外,您应该从if顶部返回,或添加else

if(n == 0) {
    printf("%s", s);
    return;
}

或者

if(n == 0) {
    printf("%s", s);
} else {
    // The rest of your code here
    ...
}

或完全删除if:这只是一种优化,没有它你的代码也可以正常工作。

n目前,当is时,您的代码将打印原始字符串两次0

如果您想让您的代码删除子字符串并返回结果,您需要分配结果,并用复制替换打印,如下所示:

char *remove_substring(char *s, int p, int n) {
    // You need to do some checking before calling malloc
    if (n == 0) return s;
    size_t len = strlen(s);
    if (n < 0 || p < 0 || p+n > len) return NULL;
    size_t rlen = len-n+1;
    char *res = malloc(rlen);
    if (res == NULL) return NULL;
    char *pt = res;
    // Now let's use the two familiar loops,
    // except printf("%c"...) will be replaced with *p++ = ...
    for (int i = 0; i < p; i++) {
        *pt++ = s[i];
    }
    for (int i = p+n; i < strlen(s); i++) {
        *pt++ = s[i];
    }
    *pt='\0';
    return res;
}

请注意,您的代码的这个新版本返回动态分配的内存,使用后需要freed 。

这是ideone 上此修改版本的演示。

于 2013-08-21T14:58:20.990 回答
2

尝试复制字符串的第一部分,然后复制第二部分

char result[10];
const char input[] = "abcdefg";

int n = 3;
int p = 4;

strncpy(result, input, p);
strncpy(result+p, input+p+n, length(input)-p-n);

printf("%s", result);
于 2013-08-21T14:58:15.963 回答
2

如果您希望在不使用strcpyor之类的函数的情况下执行此操作strncpy(我在评论中看到您说过),那么请使用类似的方法来了解strcpy(或至少一个可能的变体)如何在引擎盖下工作:

void strnewcpy(char *dest, char *origin, int n, int p) {
    while(p-- && *dest++ = *origin++)
        ;
    origin += n;
    while(*dest++ = *origin++)
        ;
}
于 2013-08-21T15:04:46.073 回答
1

你尝试了什么?不strcpy(s+p, s+p+n)工作?

编辑:固定为不依赖于未定义的行为strcpy

void remove_substring(char *s, int p, int n)
{
    p--; // 1 indexed - why?
    memmove(s+p, s+p+n, strlen(s) - n);
}

如果你真的很喜欢它,你也可以memmove用循环替换调用:

char *dst = s + p;
char *src = s + p + n;
for (int i = 0; i < strlen(s) - n; i++)
    *dst++ = *src++;

如果你这样做,你也可以去掉strlen调用:

while ((*dst++ = *src++) != '\0);

但我不确定我是否建议压缩它那么多。

于 2013-08-21T14:51:06.810 回答
1

元代码:

  • 为目的地分配一个缓冲区
  • 标出指向源字符串的指针 s
  • 推进源字符串中的指针“p-1”位置并将它们即时复制到目的地
  • 推进“n”个位置
  • 将其余部分复制到目的地
于 2013-08-21T14:55:18.337 回答