Prepending to a string

后端 未结 8 2073
北荒
北荒 2020-12-09 01:49

What is the most efficient way to prepend to a C string, using as little memory as possible?

I am trying to reconstruct the path to a file in a large directory tree.

相关标签:
8条回答
  • 2020-12-09 02:25

    You can climb to the top of the directory tree keeping the names as you go along, then paste the names together all at once. At least then you aren't doing unnecessary copies by pushing onto the front.

    int i = 0;
    int j;
    
    char temp*[MAX_DIR_DEPTH], file[LENGTH];
    
    while (some_condition) {
        temp[i++] = some_calculation_that_yields_name_of_parent_dir;        
    }
    
    char *pCurrent = file;    
    for( j = i-1; j > 0; j-- )
    {
        strcpy(pCurrent, temp[j]);
        pCurrent += strlen(temp[j]);
        *pCurrent++ = '\';
    }
    strcpy(pCurrent, filename);
    *pCurrent = 0;
    
    0 讨论(0)
  • 2020-12-09 02:36

    This solution has no more copying than necessary. It does require one strlen, so if the directory name retrieval can return the number of bytes copied or if you can precalculate the parent dir string length, you can optimize that away.

    void GetFilename(char *pFile)
    {
        strcpy(pFile, "someFile");
    }
    
    void GetParentDir(char *pDir)
    {
        strcpy(pDir, "/parentdir");
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    
        char path[1024];
        GetParentDir(path);
        int dirSize = strlen(path);
        path[dirSize] = '/';
        GetFilename(path + dirSize + 1);
        printf(path);
        return 0;
    }
    
    0 讨论(0)
  • 2020-12-09 02:42

    You could maintain the string starting from the end. Since you seem to know the maxSize already...

    So basically if file initially was (foo.txt)

    [] [] [] [] [] [f] [o] [o] [.] [t] [x] [t] [\0]
                 ^
                 |
              lastEmpty           
    

    Now if you add a parent dir a/ it will look like

    [] [] [] [a] [/] [f] [o] [o] [.] [t] [x] [t] [\0]
           ^      
           |      
        lastEmpty           
    

    So the code will look something like (there might be bugs, but you get the idea).

    char temp[LENGTH], file[LENGTH]; 
    int lastEmpty = put_at_end(some_file_name, file);  
    // lastEmpty points to right most empty slot
    
    while (some_condition) { 
        parent_dir = some_calculation_that_yields_name_of_parent_dir; 
    
        int len = strlen(parent_dir);
        char *tmp = parent_dir + len -1;
    
        while (lastEmpty > 0) {
            file[lastEmpty] = *tmp;
            lastEmpty --;
            tmp--;
        }
    } 
    

    Since I suppose we could expect parent_dir to be small, going over it twice should be ok. If you want to pass around the file string, you can just use file+lastEmpty+1.

    0 讨论(0)
  • 2020-12-09 02:45

    sprintf() is generally not 'fast'. Since you know it's pre-pending memmove() twice would probably be preferable for speed.

    If you're allocating the strings with malloc() originally you might consider using realloc() to resize the character arrays so they can contain the new string.

       char* p = malloc( size_of_first_string );
       ...
       p = realloc( p, size_of_first_string + size_of_prepended_string + 1 );
       memmove( p + size_of_prepended_string, p, size_of_first_string );
       memmove( p, prepended_string, size_of_prepended_string );
    
    0 讨论(0)
  • 2020-12-09 02:45

    I leave a buffer at left and at right of the array. You have to hold two index but if you have to do it a lot of times (otherweise there would be no problem for efficency) it worts it. The two index I suggest to be ]s;e], one included and one not:

     #define BUFSIZE 256
     #define LEFTBUF 20
     struct mstring
     {
       char * string;
       unsigned s;
       unsigned e;
      }
      void checkbuf(struct mstring *value, int newstringlen, char   leftorright)
      {
      //have fun here
      }
      char * concat (struct mstring * value, char * str)
      {
           checkbuf(value, strlen(value,str), 'r');
           int i=0;
           while (str[i])
                value->string[value->e++]=str[i++];
       }
       char * set(struct mstring * value, char * str)
       {
            value->e=LEFTBUF;
            value->s=LEFTBUF;
            concat( value,str);
    
       }
    
      char * prepend (struct mstring * value, char * str)
      {
           checkbuf(value, strlen(value,str), 'l');
           int i=strlen(value,str)-1;
           while (i>=0)
                value->string[--value->s]=str[i--];
       }
      int main()
      {
          struct mstring * mystring= (struct mstring *) malloc(sizeof(struct mstring) );
          mystring->string=(char*)malloc(sizeof(char)*BUFSIZE);
          set( mystring,"World");
          prepend(mystring,"Hallo")
    
      }
    

    then you have to prepare a function for fill substrings...

    0 讨论(0)
  • 2020-12-09 02:46

    If you don't need the string to be stored in order, but only appear to be in order, then use a thing called a "rope." (It's made of lots of "string", see.)

    I believe it's basically a vector (in C terms, an array) of struct { char *begin; char *end };

    In C++ it implements all the std::string functions. In C you'd need to write (or get a library of) replacement functions for all the strxxx() functions.

    What the "rope" would do to prepend a string to another string is simply insert a new begin,end pair pointing at the new piece of string. It might also have to copy the new piece of string, if it's a temporary pointer. Or it can just take ownership of the string if it's an allocated string.

    A rope is very good for large strings. But anything under about 8 KB is faster to handle with memmove and memcpy.

    0 讨论(0)
提交回复
热议问题