前置字符串

时间:2010-02-24 17:45:04

标签: c string concatenation

使用尽可能少的内存来预占C字符串的最有效方法是什么?

我正在尝试重建大目录树中文件的路径。

以下是我以前做过的事情的想法:

char temp[LENGTH], file[LENGTH];
file = some_file_name;

while (some_condition) {
    parent_dir = some_calculation_that_yields_name_of_parent_dir;
    sprintf(temp, "%s/%s", parent_dir, file);
    strcpy(file, temp);
}

这看起来有点笨拙。

任何帮助将不胜感激。谢谢!

8 个答案:

答案 0 :(得分:14)

如果您希望在同一个内存块中进行复制,则很难避免复制。如果分配的块足够大,你可以使用memmove将原始字符串移动你想要的长度,然后将其复制到开头,但我怀疑这是不那么“笨重”。然而,它会为你节省额外的内存(再次,授予原始块有足够的可用空间)。

这样的事情:

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


/* Prepends t into s. Assumes s has enough space allocated
** for the combined string.
*/
void prepend(char* s, const char* t)
{
    size_t len = strlen(t);
    size_t i;

    memmove(s + len, s, strlen(s) + 1);

    for (i = 0; i < len; ++i)
    {
        s[i] = t[i];
    }
}


int main()
{
    char* s = malloc(100);
    strcpy(s, "file");
    prepend(s, "dir/");

    printf("%s\n", s);
    return 0;
}

答案 1 :(得分:7)

如果您不需要按顺序存储字符串,但只有出现才能按顺序存储,那么请使用称为“绳索”的东西。 (它由许多“字符串”组成,请参阅。)

我认为它基本上是struct { char *begin; char *end };

的向量(用C术语,数组)

在C ++中,它实现了所有std :: string函数。在C中,您需要为所有strxxx()函数编写(或获取库)替换函数。

“绳索”将字符串添加到另一个字符串所做的只是插入一个新的开始,结束对指向新的字符串。它可能还必须复制新的字符串,如果它是一个临时指针。或者如果它是一个已分配的字符串,它可以获取字符串的所有权。

绳子非常适合大弦。但是,使用memmove和memcpy处理大约8 KB以下的任何内容都会更快。

答案 2 :(得分:3)

sprintf()通常不是'快'。因为你知道它的预先存在的memmove()两次可能比速度更好。

如果您最初使用malloc()分配字符串,则可以考虑使用realloc()来调整字符数组的大小,以便它们可以包含新字符串。

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

答案 3 :(得分:1)

您可以从结尾开始维护字符串。因为你似乎已经知道了maxSize ......

所以基本上如果文件最初是(foo.txt)

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

现在,如果您添加父目录/它将显示为

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

所以代码看起来像(可能有错误,但你明白了)。

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

因为我认为我们可以期望parent_dir很小,所以重复两遍应该没问题。如果要传递文件字符串,可以使用file+lastEmpty+1

答案 4 :(得分:1)

您可以爬到目录树的顶部,随时保留名称,然后将名称一次粘贴到一起。至少那时你不会通过推到前面来做不必要的副本。

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;

答案 5 :(得分:1)

也许我很困惑,但我相信前缀与换掉字符串相同。因此,字符串“World”可以追加到“Hello”,而不是“Hello”添加到“World”中:

const char world[] = "World";
const char hello[] = "Hello";

// Prepend hello to world:
const unsigned int RESULT_SIZE = sizeof(world) + sizeof(hello) + 2 * sizeof('\0');
char * result = malloc(RESULT_SIZE);
if (result)
{
  strcpy(result, hello);
  strcat(result, world);
  puts("Result of prepending hello to world: ");
  puts(result);
  puts("\n");
}

此外,执行时间的主要浪费是找到字符串的结尾。如果字符串以长度存储,则可以更快地计算结束。

答案 6 :(得分:0)

此解决方案不再需要复制。它确实需要一个strlen,因此如果目录名称检索可以返回复制的字节数,或者如果可以预先计算父目录字符串长度,则可以对其进行优化。

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

答案 7 :(得分:0)

我在数组的左侧和右侧留下一个缓冲区。你必须持有两个索引,但是如果你必须做很多次(其他方面没有效率问题)它会对它进行排序。我建议的两个索引是s; e],一个包括,一个不包括:

 #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")

  }

然后你必须为填充子串准备一个函数......