Слишком много кода для конкатенации строк в C - PullRequest
1 голос
/ 27 декабря 2011

В моем коде много строк strcat.Есть ли лучший способ объединить строки в C?

char material[50]; // it is defined before this code.
char result[10000];
strcpy(result, "// Assign new material to worldGlobe\n");
strcat(result, "shadingNode -asShader lambert -n ");
strcat(result, material);
strcat(result, ";\n");

Ответы [ 7 ]

11 голосов
/ 27 декабря 2011

Вы можете использовать строку форматирования вместе с snprintf() (безопасно по сравнению с sprintf()):

snprintf(result, 10000,
    "// Assign new material to worldGlobe\nshadingNode -asShader lambert -n %s;\n",
    material);
3 голосов
/ 27 декабря 2011

strcat действительно подходит только для очень маленьких струн;у него есть несколько проблем для чего-то нетривиального, например:

  • Из-за проблемы Schlemiel The Painter , strcat равно O (n) по всей длине вводато есть, чем длиннее ваши строки, тем дольше каждая конкатенация занимает.Это потому, что strcat должен пройти всю строку, чтобы найти ее конец.Чтобы решить эту проблему, сохраните длину строки вместе со строковыми данными, что позволит вам перейти непосредственно к концу строки.
  • Он не выполняет никакой проверки границ.Если вы слишком много strcat окажетесь в конце строки, она с радостью запишет за конец строки, в результате в лучшем случае будет возникать ошибка сегмента, в худшем - серьезная уязвимость системы безопасности, и, скорее всего, возникнут некоторые ошибки, которые могут сделать васударись головой о стену.strncat частично решает эту проблему, если вы передадите ему правильный размер буфера назначения.
  • Если целевой буфер слишком мал, ни strcat, ни strncat не увеличат его размер: выВам придется сделать это самостоятельно.

В вашей ситуации есть два практических решения:

a) Алгоритм Tower of Hanoi: создайте стек строк.Если новая строка короче вершины стека, поместите ее в стек.Если он длиннее, вытолкните сверху, объедините и повторите процесс с результатом.Когда вы закончите нажимать, объедините содержимое стека.Это то, что std::stringstream в C ++ или StringBuilder в .NET, и если вы посмотрите вокруг, я уверен, что вы найдете подходящую реализацию в C.

b) Напишите свои строки непосредственно впоток.То, что вы выводите, очень похоже на код - почему бы не записать его в файл напрямую?

2 голосов
/ 27 декабря 2011

А как же

sprintf(result, "// Assign new material to worldGlobe\nshadingNode -asShader lambert -n %s;\n\0", material);
1 голос
/ 28 декабря 2011

Попробуйте stpcpy; см ссылка . Ваш пример кода становится:

   char material[50]; // it is defined before this code.
   char result[10000], *p = result;
   p = stpcpy(p, "// Assign new material to worldGlobe\n");
   p = stpcpy(p, "shadingNode -asShader lambert -n ");
   p = stpcpy(p, material);
   p = stpcpy(p, ";\n");

Эта функция доступна в Linux; страница man для stpcpy в моей системе сообщает:

Эта функция не является частью стандартов C или POSIX.1 и не является обычной для систем Unix, но также не является изобретением GNU. Возможно, это исходит от MS-DOS.

Если у вас его нет, достаточно просто написать:

   char *stpcpy(char *restrict dst, const char *restrict src) {
      return strcpy(dst, src) + strlen(src);
   }

Это предполагает, что вы знаете об опасностях strcpy .

0 голосов
/ 28 декабря 2011

Было бы довольно просто построить структуру строкового буфера, которая будет отслеживать текущую позицию в вашем буфере, и объединить ее с vsprintf, чтобы получить catf(). Функция vsnprintf() (при условии, что она доступна) аналогична printf, за исключением того, что она принимает va_list вместо ... после строки форматирования.

Этот подход имеет преимущество перед другими ответами, заключающееся в том, что он позволяет вам 'cat' из любого места в вашем коде, у которого есть доступ к структуре, без явного переноса текущей длины или пересчета каждый раз, как это делает strcat.

Вот примерный набросок бесплатно .....

/* Note: the typedef is for the pointer, not the struct. */
typedef struct StrBufStruct { 
   char * buffer, 
   size_t size, 
   size_t pos 
} * StrBuf;

/* Create a new StrBuf. NOTE: Could statically allocate too. */
StrBuf newStrBuf(size_t size){
   StrBuf sb;
   sb = malloc( sizeof(struct StrBufStruct) );
   sb->size = size;
   sb->pos = 0;
   sb->buffer = malloc( size );
   /* TODO: ALWAYS CHECK YOUR MALLOC!!! */
}

int sbcatf( StrBuf b, char * fmt, ... )
{
   va_list ap;
   int res;
   if( b->pos < b->size )
   {  
      va_start(ap,fmt);
      res = vsnprintf( b->buffer[b->pos], b->size - b->pos, fmt, ap );
      b->pos += res;
      va_end();
   } else {
     /* If you want to get really fancy, use realloc so you don't have to worry 
       about buffer size at all.  But be careful, you can run out of memory. */
   }
}

/* TODO: Write a free/delete function */

int main(int argc, char **argv){
   int i;
   /* initialize your structure */
   StrBuf sb = newStrBuf(10000);

   /* concatenate numbers 0-999 */
   for(i=0; i < 1000; i++){
      sbcatf(sb, "I=%d\n", i);
   }

   /* TODO: whatever needs to be done with sb->buffer */

   /* free your structure */
   deleteStrBuf(sb);
}

Также обратите внимание, что если все, что вы пытаетесь сделать, это сделать действительно длинную строку, но хотите иметь возможность иметь разрывы строк в своем коде, это обычно приемлемо, хотя я лично не гарантирую переносимость. Я также использую эту технику для разделения строк на "\n" разрывов строк, чтобы код выглядел так, как будет выглядеть результирующая строка.

const char * someString = "this is one really really really really"
    "long stttttttttrrrrrrrrrrrrrrrrrrrriiiiiiiiiiinnnnnnngggggg"
    " because the compiler will automatically concatenate string"
    " literals until we reach a ';' after a \" character";
0 голосов
/ 27 декабря 2011

Вы можете иметь функцию, которая возвращает указатель на конец строки, и использовать этот указатель конца в будущих вызовах.Это устранит кучу лишних вещей «сначала найди конец строки».

char* faster_cat(char* dest, const char* src)
{
     strcpy(dest, src);
     return dest + strlen(src);
}

Используйте как:

char result[10000];
char *end = &result[0];
result[0] = '\0';  // not strictly necessary if you cat a string, but why not
end = faster_cat(end, "// Assign new material to worldGlobe\n");
end = faster_cat(end, "shadingNode -asShader lambert -n ");
end = faster_cat(end, material);
end = faster_cat(end, ";\n");

// result now contains the whole catted string
0 голосов
/ 27 декабря 2011

C - это в основном самодельный язык.

Теперь, когда вы знаете как объединять строки, вы должны написать свою собственную функцию, чтобы упростить ее.

Я бы предложил что-то вроде:

char*  str_multicat(char* result, ...);

И назовите это как-то так:

str_mutlicat(result, "// Assign new material to worldGlobe\n",
                     "shadingNode -asShader lambert -n ",
                     material,
                     ";\n",
                     NULL);

(подсказка, если вы не знаете синтаксис ..., посмотрите va_arg, va_start, va_end)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...