web-dev-qa-db-ja.com

文字列の前に付ける

できるだけ少ないメモリを使用して、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);
}

これは少し不格好に見えますが。

任意の助けいただければ幸いです。ありがとう!

31
Ryan

同じメモリチャンクにコピーしたい場合、コピーを避けることはほとんどできません。割り当てられたチャンクが十分に大きい場合couldmemmoveを使用して、元の文字列を先頭に追加したい長さだけシフトし、その文字列を先頭にコピーしますが、これは疑わしいです「不格好」ではありません。ただし、追加のメモリが節約されます(ここでも、元のチャンクに両方に十分な空き領域があることは当然です)。

このようなもの:

#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;
}
14
Eli Bendersky

文字列を順番に格納する必要はなく、appearのみを順番に格納する場合は、「ロープ」と呼ばれるものを使用します。 (たくさんの「ひも」でできています。参照してください。)

基本的にはstruct { char *begin; char *end };のベクトル(C用語では配列)だと思います

C++では、すべてのstd :: string関数を実装します。 Cでは、すべてのstrxxx()関数の置換関数を記述する(またはライブラリを取得する)必要があります。

文字列を別の文字列の前に付けるために「ロープ」が行うことは、新しい文字列の部分を指す新しいbegin、endペアを挿入することだけです。一時的なポインタの場合は、新しい文字列をコピーする必要がある場合もあります。または、割り当てられた文字列の場合は、文字列の所有権を取得できます。

ロープは大きな弦に非常に適しています。ただし、約8 KB未満の場合は、memmoveとmemcpyで処理する方が高速です。

7
Zan Lynx

sprintf()は一般に「高速」ではありません。あなたが知っているので、それは前に付けられているmemmove()を2回することはおそらく速度のために好ましいでしょう。

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
Jay

ディレクトリツリーの最上部に登り、名前を続けながら、名前をまとめて貼り付けます。少なくとも、前面に押し出して不要なコピーを行うことはありません。

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;
1
Joel

おそらく私は混乱していますが、先頭に追加することは、文字列を交換して追加することと同じだと思います。したがって、prepending "Hello"を "World"に変更する代わりに、文字列 "World"をappendedから "Hello"に変更できます。

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

また、実行時間の主な無駄は、文字列の終わりを見つけることです。文字列が長さで格納されている場合、終わりはより速く計算できます。

1
Thomas Matthews

文字列を最後から維持することができます。あなたはすでに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が小さいと予想できると思うので、2回オーバーしても大丈夫です。ファイル文字列を渡したい場合は、file+lastEmpty+1

1
Aryabhatta

配列の左側と右側にバッファを残します。 2つのインデックスを保持する必要がありますが、それを何度も実行する必要がある場合(そうでない場合は、効率に問題はありません)、それが問題になります。私が] s; e]であると提案する2つのインデックスは、1つは含まれ、1つは含まれません。

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

  }

次に、部分文字列を埋めるための関数を準備する必要があります...

0
jurhas

このソリューションは、必要以上にコピーすることはありません。 strlenは1つ必要なので、ディレクトリ名の取得でコピーされたバイト数を返すことができる場合、または親dir文字列の長さを事前に計算できる場合は、それを最適化できます。

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