0

I'm trying to replace ' ' (space) with '___' (triple underscore) in C.

Here is my code:

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

int main()
{
   char *a = "12 34 56";
   int a_l = strlen(a);
   printf("str1: \"%s\" (%d)\n", a, a_l);

   char *b = "___";
   int b_l = strlen(b);
   printf("str2: \"%s\" (%d)\n", b, b_l);

   for (int i = 0; i < a_l; i++) {
      if (a[i] == ' ') {
         char *o = malloc(a_l + b_l);

         strncpy(o, a, i);
         strncpy(o + i, b, a_l);
         //strncpy help

         printf("out:  \"%s\"\n", o);
      }
   }

   return 0;
}

I think that it is right so far, but I need to replace the comment line with correct strncpy (take the rest of string a (excluding space) and append it to string o). So the output should be like this:

str1: "12 34 56" (8)
str2: "___" (3)
out:  "12___34 56"
out:  "12 34___56"

If there are other mistakes in my code, please tell me.

UPD: This shouldn't replace all spaces in a loop. If the source string contains 8 spaces, there should be 8 lines printed and in each line only one space should be replaced.

tpimh
  • 446
  • 2
  • 9
  • 23
  • Take a look at strtok function. – Varvarigos Emmanouil Jun 30 '13 at 20:52
  • @V_Maenolis good idea, but as `strtok()` modifies the string, it won't work here on `a` which points to the literal/fixed `"12 34 56"`. – meaning-matters Jun 30 '13 at 20:53
  • 1
    In your code sample you make two o strings You ll have either to count first the space characters in your string then to allocate proper space to hold the new string or to realloc the o string for exery space you find. – Varvarigos Emmanouil Jun 30 '13 at 20:59
  • Guys, you completely misunderstand what output should look like. All the answers are useful, but their aim is to replace everything in one loop. This code shouldn't replace all spaces with triple underscore. If the source string contains 8 spaces, there should be 8 lines printed and in each line only one space should be replaced. – tpimh Jul 01 '13 at 05:36

6 Answers6

6

You are overcomplicating this so much that I just TL;DR.

Some remarks that you might surely want to read, learn, embrace well and use:

I. int is not for string lengths and stuff. size_t is for string lengths and stuff.

II. String literals cannot be modified, so using the legacy char * type for assigning them to a variable is no good by any means, const-qualify that poor pointer base type.

III. Use VLAs instead of malloc() if dynamic memory management is not really needed (we're not living in 1989 anymore).

IV. NUL-terminate your strings because C stdlib routines expect you to do so.

int main()
{
    const char *in = "foo bar baz";
    int nspc = 0;

    for (const char *p = strchr(in, ' '); p; p = strchr(p + 1, ' '))
        nspc++;

    char buf[strlen(in) + nspc * 2 + 1];
    memset(buf, 0, sizeof(buf));

    const char *s = in;
    for (const char *p = strchr(s, ' '); p; p = strchr(s, ' ')) {
        strncat(buf, s, p - s);
        strcat(buf, "___");
        s = p + 1;
    }

    const char *end = in + strlen(in);
    strncat(buf, s, end - s);

    printf("%s\n", buf);
    return 0;
}
  • Oh, and yes, this is seriously suboptimal (the loop runs over the input string twice, the repeated calls to `strcat()` make the concatenation run in `O(n ^ 2)` instead of `O(n)`), but until the strings are shorter than a few dozens of kilobytes, this "performance issue" is not really an issue at all. If you need to do so, benchmark and optimize it. –  Jun 30 '13 at 21:15
  • @RamyAlZuhouri Char-by-char? Surely I don't want that while we can haz stdlib functions from ``. Seriously, that's your reason for the downvote? –  Jun 30 '13 at 21:42
  • @RamyAlZuhouri: Can it be done with a single loop while allocating memory only once? – Ry- Jun 30 '13 at 21:43
  • @minitech I'm afraid not quite, **except** if one aims to do the exponential storage expansion trick, but I was too tired to implement that... –  Jun 30 '13 at 21:47
  • @H2CO3: Exponential expansion isn’t “only once” :) Anyways, I’m pretty sure it can’t either, just making sure. Only one loop sounds nicer, but only if it can do the same thing. – Ry- Jun 30 '13 at 21:49
  • 1
    @minitech I think the downvoter hasn't considered that C is not Pascal, so `strlen()` loops through the string too. –  Jun 30 '13 at 21:49
  • This, or you can allocate a 3 times +1 long string, and be sure it will have enough space. But it isn't a matter of performance, it's just that your code is messy. Now be honest and don't take it personal, saying that I cannot code in C or down voting my old answers. – Ramy Al Zuhouri Jun 30 '13 at 22:16
2

You can try this one. The problem comes from the fact that a_l + b_l in your malloc is always the same value. It doesn't affect by number of spaces.

int count = 0, index = 0;
for (int i = 0;  i < a_l;  ++i) {
    if (a[i] == ' ') {
        count++;
    }
}

const char *o = malloc(a_l + 2 * count + 1); // 2 is because you add 3 new symbols, but remove 1 so 3 - 1 = 2
memset(o, 0, sizeof(o));

for (int i = 0;  i < a_l;  ++i) {
    if (a[i] != ' ')
        o[index++] = a[i];
    else {
        o[index++] = '_';
        o[index++] = '_';
        o[index++] = '_';
    }
}
Kamen Stoykov
  • 1,715
  • 3
  • 17
  • 31
  • Surely `malloc()` is not needed here since we stay inside `main()`... Also, where's the `NUL`-terminator? –  Jun 30 '13 at 21:09
  • Yes, you are completely right. I have missed NULL terminator and have used malloc, cos I was following the example code. – Kamen Stoykov Jun 30 '13 at 21:11
2

Without using library function!!!

while(*string)
        {
                  if(*string=='\\')
                {
                        *string++;
                        while(repl_len--)
                         *dest++ = *repl_string++;
                }               
                else
                {
                        *dest++ = *string++;
                }
           repl_string = temp_repl_string;
           repl_len = temp_repl_len;    
        }
Prakash GiBBs
  • 297
  • 2
  • 13
0
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

size_t my_strlen(const char *str, size_t *spc){
    size_t len;
    *spc = len = 0;
    while(*str){
        ++len;
        if(*str++ == ' ')++*spc;
    }
    return len;
}

int main(void){
   char *a = "12 34 56";
   size_t spc;
   int a_l = my_strlen(a, &spc);
   printf("str1: \"%s\" (%d)\n", a, a_l);

   char *b = "___";
   int b_l = strlen(b);
   printf("str2: \"%s\" (%d)\n", b, b_l);

   char *p, *o = malloc(a_l - spc + spc * b_l + 1);
   p=o;
   for (int i = 0; i < a_l; i++) {
      if(a[i] == ' ') {
         strncpy(p, b, b_l);
         p += b_l;
      } else {
         *p++ = a[i];
      }
   }
   *p = '\0';
   printf("out:  \"%s\"\n", o);
   free(o);
   return 0;
}
BLUEPIXY
  • 39,699
  • 7
  • 33
  • 70
  • why `malloc()`? Why `int` (instead of the correct `size_t`) for string lengths? Where's the `const` before pointers that ought to point to `const char`? –  Jun 30 '13 at 21:10
  • 1
    i don't know what are you saying. – BLUEPIXY Jun 30 '13 at 21:15
  • @H2CO3 You are right, `size_t` should be used instead, although I think you are being a little paranoid, the use of `size_t` is no mandatory whatsoever. For example in my computer with an `int` I can hold up to 2GiB of data, IMO thats more than enough; unless you work at Google. It's all about Integer Overflow, IMHO with today's hardware this should not perturb you from sleeping at night. http://stackoverflow.com/questions/6004415/why-size-t-when-int-would-suffice – yeyo Jul 01 '13 at 03:01
0

I see that many answers have been added, but this may have been done very simply with a single loop. Since the string is very short, you can sacrifice memory over CPU and allocate an array 3 times +1 bigger than the original string, and be sure that it won't be overflowed:

const char* string= "12 34 56";
size_t length= strlen(string);
char result[length*3 +1];
unsigned int index= 0;
memset(result, '\0', length*3+1);
for(int i=0; i<length; i++)
{
    if(string[i]!= ' ')
    {
        result[index++]= string[i];
    }
    else
    {
        strcat(result,"___");
        index+= 3;
    }
}
Ramy Al Zuhouri
  • 21,580
  • 26
  • 105
  • 187
0

I found another thread and after reading the answers, I figured out that the right line should look like this:

strncpy(o + i + b_l, a + i + 1, a_l - i);

And after few fixes that were suggested in this thread my code looks like this:

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

int main()
{
   char *a = "12 34 56";
   size_t a_l = strlen(a);
   printf("str1: \"%s\" (%d)\n", a, a_l);

   char *b = "___";
   size_t b_l = strlen(b);
   printf("str2: \"%s\" (%d)\n", b, b_l);

   for (int i = 0; i < a_l; i++) {
      if (a[i] == ' ') {
         char *o = malloc(a_l + b_l);

         strncpy(o, a, i);
         strcpy(o + i, b);
         strncpy(o + i + b_l, a + i + 1, a_l - i);

         printf("out:  \"%s\"\n", o);
             free(o);
      }
   }

   return 0;
}

And this prints the desired putput.

Community
  • 1
  • 1
tpimh
  • 446
  • 2
  • 9
  • 23