0

I wrote the following code with the intention of using pointer arithmetic on strings to find, and replace, a targeted substring. Obviously, it's not elegant, but unfortunately it's also incorrect - it adds extraneous characters to the string.

#include <stdio.h>
#include <string.h>

int main() {
    char string[] = "The quick brown fox jumped over the lazy dog.";
    char target[] = "brown"
    char replacement[] = "ochre";
    char segment[80+1];
    char pre_segment[80+1];
    char post_segment[80+1];
    int S = strlen(string), T = strlen(target);
    for (int i = 0; i < S; i++) {
        strncpy(segment, string + i, T);
        if (strcmp(segment, target) == 0) {
        >>> strncpy(pre_segment, string, i); <<<
            strncpy(post_segment, string + i + T,
                S - (i + T));
            strcat(pre_segment, replacement);
            strcat(pre_segment, post_segment);
            printf("%s\n", pre_segment);
        }
    }
    return 0; 
}

After the line marked like >>>this<<<, extraneous characters have been prepended to replacement before replacement is concatenated with pre_segment.

Can someone give me suggestions on how to debug this? (Suggestions for a nicer solution are also welcome, but please try to be explicit. Also, I'm not supposed to be using dynamic memory allocation for this.)

Chris
  • 215
  • 1
  • 12
  • "how to debug this"? Same way you would debug most programs. Use a debugger to step thru the code and examine state as you go. – kaylum Jun 21 '17 at 22:58
  • @kaylum I can tell you where it's going wrong; I was hoping someone more knowledgeable could help me understand *why*. – Chris Jun 21 '17 at 23:00
  • 1
    Then why don't you tell us where it is going wrong? That would be useful info - so that we don't have to debug that ourselves and also to show you have done it. And you did explicitly ask "how to debug this". – kaylum Jun 21 '17 at 23:01
  • After the line strncpy(pre_segment, string, i), extraneous characters are appended along with the replacement string, in front of the replacement string. (The program I provided is an MCVE, so you can see this for yourself if you compile it.) – Chris Jun 21 '17 at 23:03
  • 1
    This code produces no output for me.... Also, missing a semi-colon. And `strlen()` returns a `size_t` value, not an `int`. – ad absurdum Jun 21 '17 at 23:07
  • Why do you declare in your code `80+1` instead of `81`? Same result of @DavidBowling – Thecave3 Jun 21 '17 at 23:13
  • 2
    It does not help that the posted code does not produce output as described in the question. But, `strncpy()` does not always write a `\0`, and this is one of those cases. You need to add it yourself. By adding ` segment[T] = '\0';` after `strncpy(segment, string + i, T);`, the program seems to behave as expected. – ad absurdum Jun 21 '17 at 23:39
  • 1
    Your for loop is iterating over each of the single characters in `string`. Then for each of those characters, you are performing 6 (7 if you count `printf`) whole-string level operations instead of character-level operations. Time to re-think the whole design. – Lee Daniel Crocker Jun 22 '17 at 00:19
  • Possible duplicate of [strncpy question (C language)](https://stackoverflow.com/questions/4483362/strncpy-question-c-language) – Ken Y-N Jun 22 '17 at 04:22

3 Answers3

5

Don't use strncpy. It almost certainly doesn't do what you think it does. In particular, it doesn't guarantee NUL-termination, while fooling people into thinking that it does. If you want to copy exactly n characters, use memcpy(dest, src, n); and then explicitly NUL-terminate with dest[n] = '\0';. The lack of NUL termination is likely to be causing your problems. (Check that in your debugger!)

However, there is no need to do the strncpy at all. Use strncmp or memcmp. (Only use memcmp if you know that there are at least strlen(target) bytes left in the string.) If strlen(target) bytes starting at some point in string match target, then you've found a match.

Even better would be to use strstr to find the next occurrence of the string.

rici
  • 234,347
  • 28
  • 237
  • 341
  • Thank you so much. I'll be right back after trying this! – Chris Jun 21 '17 at 23:11
  • Your suggestion worked exactly for me, so if you don't mind, can I ask some follow up questions? (1) How did you know that was what was wrong, and (2) how would you identify this exact problem in a debugger? (i.e., what would it "look like"?) – Chris Jun 21 '17 at 23:16
  • 1
    @chris: "adds extraneous characters" often means an unterminated string, and misuse of `strncpy` often results in unterminated strings. So I started with a bias based on experience. You will see it in a debugger by seeing that strlen gives an unexpected value (or segfaults), or that extraneous characters are printed, or by looking for the NUL terminator and not finding it. `strncpy` is designed for use with packed fixed-length database fields, but there had grown up a mythology that it is somehow safer than strcpy. So it's always worth looking at. – rici Jun 22 '17 at 00:07
1

You should always try to split your code into smaller parts(functions), we can identify two key steps in the process of replacing a substring, finding the substring and then replacing it. This is a solution i am proposing for you, it's been three years since i wrote my last c line of code so this is not perfect but it does the job:

#include <stdio.h>
#include <string.h>


int find_occurence(char* original, char *target){
  size_t counter = 0;
  char *index = original;
  do{
    while(target[counter] == index[counter])
      counter++;
    if (counter >= strlen(target))
      return (int)(index-original);
    else
      counter = 0;
  }
  while(*(index++));
  return -1;
}

void replace(char *original, char *target, char *replacement,char *destination){
  int index = find_occurence(original, target);
  if (index == -1)
  {
    strncpy (destination, original, strlen(original)+1 );
    return;
  }

  char *last_part;

  //Copy the string before target
  strncpy (destination, original, index );

  //Copy the replacement
  strncpy (&destination[index], replacement, strlen(replacement) );

  //Extract the part after the target
  last_part = &original[index+strlen(target)];

  //Copy the part after the target plus the \0 character
  strncpy (&destination[index+strlen(replacement)],last_part,strlen(last_part)+1);
}


int main() {
  char *original = "I want to replace literally this by that";
  char *target = "this";
  char *replacement = "that";
  char destination[100];

  replace(original,target,replacement, destination);

  printf("%s\n",destination);


}
SEDaradji
  • 1,348
  • 15
  • 18
0

I can not agree with @rici that exclaims that the function strncpy should not be used. Any function can be used incorrectly. And strncpy is not an exception. You should keep in mind that it is not necessary that the function will copy a string. So you have yourself to append explicitly a zero-character to the copied sequence of characters.

And you forgot to do this.

Though your implementation is too complicated and confusing nevertheless in any case it should be written carefully.

Here is an updated version of your program. Pay attention to these statements

segment[T] = '\0';
pre_segment[i] = '\0';
post_segment[S - ( i + T )] = '\0';

Or if you prefer tp use pointers then you could write

*( segment + T ) = '\0';
*( pre_segment + I ) = '\0';
*( post_segment + S - ( i + T )) = '\0';

Here you are.

#include <stdio.h>
#include <string.h>

int main(void) 
{
    char string[] = "The quick brown fox jumped over the lazy dog.";
    char target[] = "brown";
    char replacement[] = "ochre";
    char segment[80+1];
    char pre_segment[80+1];
    char post_segment[80+1];

    size_t S = strlen(string), T = strlen(target);

    for ( size_t i = 0; i < S; i++) 
    {
        strncpy(segment, string + i, T);
        segment[T] = '\0';

        if ( strcmp( segment, target ) == 0 ) 
        {
            strncpy(pre_segment, string, i);
            pre_segment[i] = '\0';

            strncpy( post_segment, string + i + T, S - (i + T));
            post_segment[S - ( i + T )] = '\0';

            strcat(pre_segment, replacement);
            strcat(pre_segment, post_segment);
            printf("%s\n", pre_segment);
        }
    }

    return 0;
}

The program output is

The quick ochre fox jumped over the lazy dog. 
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 3
    I did not read the comments of @rici to mean that `strncpy()` should never be used; it is certainly misused often. There is a knee-jerk tendency among some to want to turn any instance of `strcpy()` into `strncpy()` in the interest of "safety", and this is not always the right choice. – ad absurdum Jun 21 '17 at 23:43
  • I, uh, incremented your reputation (this site hates comments like that apparently). I appreciate you spelling out the importance of the null characters after the copying. – Chris Jun 22 '17 at 00:46