简体   繁体   中英

remove a specified number of characters from a string in C

I can't write a workable code for a function that deletes N characters from the string S, starting from position P. How you guys would you write such a function?

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]);
    }


}

Example:

s: "abcdefghi"
p: 4
n: 3

output:

abcghi

But for a case like n = 0 and p = 1 it's not working! Thanks a lot!

A few people have shown you how to do this, but most of their solutions are highly condensed, use standard library functions or simply don't explain what's going on. Here's a version that includes not only some very basic error checking but some explanation of what's happening:

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;
}

One caveat to watch out for: please don't call this function like this:

remove_substr("abcdefghi", 4, 3);

Or like this:

char *s = "abcdefghi";

remove_substr(s, 4, 3);

Doing so will result in undefined behavior, as string literals are read-only and modifying them is not allowed by the standard.

Strictly speaking, you didn't implement a removal of a substring: your code prints the original string with a range of characters removed.

Another thing to note is that according to your example, the index p is one-based, not zero-based like it is in C. Otherwise the output for "abcdefghi", 4, 3 would have been "abcdhi" , not "abcghi" .

With this in mind, let's make some changes. First, your math is a little off: the last loop should look like this:

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

Demo on ideone.

If you would like to use C's zero-based indexing scheme, change your loops as follows:

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

In addition, you should return from the if at the top, or add an else :

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

or

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

or remove the if altogether: it's only an optimization, your code is going to work fine without it, too.

Currently, you code would print the original string twice when n is 0 .

If you would like to make your code remove the substring and return a result, you need to allocate the result, and replace printing with copying, like this:

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;
}

Note that this new version of your code returns dynamically allocated memory, which needs to be free d after use.

Here is a demo of this modified version on ideone .

Try copying the first part of the string, then the second

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);

If you are looking to do this without the use of functions like strcpy or strncpy (which I see you said in a comment) then use a similar approach to how strcpy (or at least one possible variant) works under the hood:

void strnewcpy(char *dest, char *origin, int n, int p) {
    while(p-- && *dest++ = *origin++)
        ;
    origin += n;
    while(*dest++ = *origin++)
        ;
}

What did you try? Doesn't strcpy(s+p, s+p+n) work?

Edit: Fixed to not rely on undefined behaviour in strcpy :

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

If your heart's really set on it, you can also replace the memmove call with a loop:

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

And if you do that, you can strip out the strlen call, too:

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

But I'm not sure I recommend compressing it that much.

metacode:

  • allocate a buffer for the destination
  • decalre a pointer s to your source string
  • advance the pointer "p-1" positions in your source string and copy them on the fly to destination
  • advance "n" positions
  • copy rest to destination

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM