Thread: Extend parser functionality without realloc

  1. #1
    Registered User
    Join Date
    Sep 2006
    Posts
    35

    Extend parser functionality without realloc

    Code:
    #include <ctype.h>
    #include <stdio.h>
    #include <string.h>
    
    int main(void) {
            const char* line = "user1,user2,&,user4 group1,group2,group3 string1 string2 string3";
            const char* user = "user3";
            const char* end = line;
            const char* start;
            char* field;
            size_t len;
    
            while (isspace(*end)) ++end;
            start = end;
            while (*end != '\0' && !isspace(*end)) ++end;
            len = end - start;
            field = malloc(len + 1);
            sprintf(field, "%.*s", len, start);
            printf("%s\n", field);
            free((void*)field);
    }
    At the moment, the above program outputs "user1,user2,&,user4." How can I modify it so that it replaces & with char* user (preferably in the while block) without using realloc? Or if it cannot work without realloc, how can I correctly use it in this situation? The output should look like "user1,user2,user3,user4." Thanks in advance for any help.

  2. #2
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Code:
    free((void*)field);
    You don't need to cast that, but you do need to include <stdlib.h>.

    You could use a combination of strstr()/strchr() and memmove()/whatever and realloc()/malloc(). (You can do it without realloc() by calculating how much memory you need for the initial malloc(). len - 1 ["&"] + 5 ["user5"] + 1 [NULL].)
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  3. #3
    Registered User
    Join Date
    Sep 2006
    Posts
    35
    Quote Originally Posted by dwks
    Code:
    free((void*)field);
    You don't need to cast that, but you do need to include <stdlib.h>.

    You could use a combination of strstr()/strchr() and memmove()/whatever and realloc()/malloc(). (You can do it without realloc() by calculating how much memory you need for the initial malloc(). len - 1 ["&"] + 5 ["user5"] + 1 [NULL].)
    Thanks, dwks. I understand how to implement the memory allocation part now, but I'm still stumped when it comes to implementing the actual replacement. Could you please clarify that a bit?

  4. #4
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    Think about creating a few separate helper functions which do one very specific task.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  5. #5
    Registered User
    Join Date
    Sep 2006
    Posts
    35
    Quote Originally Posted by Salem
    Think about creating a few separate helper functions which do one very specific task.
    That's definitely not difficult to change, but I'm having trouble coming up with the actual implementation and code for the '&' swap, even with the correct memory allocation.

  6. #6
    Just Lurking Dave_Sinkula's Avatar
    Join Date
    Oct 2002
    Posts
    5,005
    Something to look at. Or
    Code:
    #include <ctype.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    void parse(const char *line, const char *user)
    {
       const char* end = line;
       const char* start;
       char* field;
       size_t len, replen;
    
       while ( isspace(*end) ) ++end;
       start = end;
       while ( *end != '\0' && !isspace(*end) ) ++end;
       len = end - start;
       replen = strlen(user);
       field = malloc(replen + len);
       if ( field )
       {
          char *ampersand;
          sprintf(field, "%.*s", len, start);
          ampersand = strchr(field, '&');
          if ( ampersand )
          {
             memmove(ampersand + replen, ampersand + 1, strlen(ampersand + 1));
             memcpy(ampersand, user, replen);
          }
          printf("\"%s\"\n", field);
          free(field);
       }
    }
    
    int main(void)
    {
       parse("user1,user2,&,user4 group1,group2,group3 string1 string2 string3", "user3");
       return 0;
    }
    
    /* my output
    "user1,user2,user3,user4"
    */
    [edit]Maybe a better one to look at.
    Last edited by Dave_Sinkula; 09-17-2006 at 12:45 PM. Reason: Added second link.
    7. It is easier to write an incorrect program than understand a correct one.
    40. There are two ways to write error-free programs; only the third one works.*

  7. #7
    Registered User
    Join Date
    Sep 2006
    Posts
    35
    Thanks, Dave_Sinkula, for the actual implementation. One quick question:
    Code:
    field = malloc(replen + len);
    should look more like
    Code:
    field = malloc(replen + len + 1);
    because of the null terminator, right?

  8. #8
    Just Lurking Dave_Sinkula's Avatar
    Join Date
    Oct 2002
    Posts
    5,005
    Quote Originally Posted by Queue
    Thanks, Dave_Sinkula, for the actual implementation. One quick question:
    Code:
    field = malloc(replen + len);
    should look more like
    Code:
    field = malloc(replen + len + 1);
    because of the null terminator, right?
    Sure, but you're getting rid of one character.
    7. It is easier to write an incorrect program than understand a correct one.
    40. There are two ways to write error-free programs; only the third one works.*

  9. #9
    Registered User
    Join Date
    Sep 2006
    Posts
    35
    I just realized that each field could possibly have more than just one ampersand. How would I take this into account if I were to modify post #6? A simple while loop would not work, as the memory needs to be reallocated as well. The following is my attempt; please correct me if I did something wrong.
    Code:
    #include <ctype.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    void parse(const char *line, const char *user)
    {
       const char* end = line;
       const char* start;
       char* field;
       int ampersand_count = 0;
       size_t len, replen;
    
       while ( isspace(*end) ) ++end;
       start = end;
       while ( *end != '\0' && !isspace(*end) ) ++end;
       len = end - start;
       replen = strlen(user);
       field = malloc(len + 1);
       if ( field )
       {
          char *ampersand;
          sprintf(field, "%.*s", len, start);
          ampersand_count = 0;
          const char* traverse = start;
          while (traverse != end) {
             if (*traverse == '&') ++ampersand_count;
             ++traverse;
          }
          free(field);
          field = malloc(len + ampersand_count * (userlen - 1) + 1);
          if (field) {
             sprintf(field, "%.*s", len, start);
             while ((ampersand = strchr(field, '&'))) {
                memmove(ampersand + replen, ampersand + 1, strlen(ampersand + 1));
                memcpy(ampersand, user, replen);
             }
          }
          printf("\"%s\"\n", field);
          free(field);
       }
    }
    
    int main(void)
    {
       parse("user1,user2,&,user4 group1,group2,group3 string1 string2 string3", "user3");
       return 0;
    }
    
    /* my output
    "user1,user2,user3,user4"
    */
    I'm not particularly fond of this solution, primarily because I must traverse the string twice (once through char* traverse, the other through strchr). Can anyone post a cleaner or faster solution?
    Last edited by Queue; 09-17-2006 at 09:42 PM.

  10. #10
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    Unless you use realloc, you're going to have to go through your string twice anyway. Once to get its length, once to remove anything. Why don't you just count the number of characters you're removing when you go through to count its length? The only way to avoid going through it twice is to have a buffer that you copy characters into as you count its length. I'll let you figure out how to get around the possibility that your buffer is smaller than the passed string, and how to get around it without going through the string more than once, without using realloc.


    Quzah.
    Hope is the first step on the road to disappointment.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. did i understood right this explantion of realloc..
    By transgalactic2 in forum C Programming
    Replies: 3
    Last Post: 10-24-2008, 07:26 AM
  2. writing a pack-style function, any advices?
    By isaac_s in forum C Programming
    Replies: 10
    Last Post: 07-08-2006, 08:09 PM
  3. using realloc
    By bobthebullet990 in forum C Programming
    Replies: 14
    Last Post: 12-06-2005, 05:00 PM
  4. Problem with a file parser.
    By Hulag in forum C++ Programming
    Replies: 7
    Last Post: 03-17-2005, 09:54 AM
  5. Parser - need help!
    By thelma in forum C Programming
    Replies: 2
    Last Post: 04-05-2004, 08:06 PM