Скажем, мы хотим объединить const char * s [0], s [1], ... s [n-1] в один длинный char out [] в C.

Формально (без учета переполнения буфера для простоты):

void concatManyStrings(char out[], const char *s[], size_t n);

Конечно, это тривиальная задача: начать с указателя на выход и продвигать его для каждого символа,
при просмотре входных строк.

Другой подход (который по-прежнему является линейным по времени) - сохранить указатель до конца,
и с каждым s [i] выполните:

{ strcpy(endp, s[i]); endp += strlen(s[i]); }

Но код был бы чище, если бы существовала стандартная функция CRT, которая знает, как strcpy(),
и вернуть количество скопированных символов (или, что эквивалентно, указатель на следующий символ после скопированного).

Единственная функция CRT, которую я могу придумать, это sprintf(), но это явно не
столь же эффективен, как простой strcpy(), который возвращает счетчик.

Есть такая функция, которую мне не хватает?

1
Naftali Ben-Hispusit 25 Авг 2011 в 23:36

3 ответа

Лучший ответ

strlcpy() и strlcat(), к сожалению, нестандартные, но если они у вас есть, вы можете использовать их для этого. Оба они возвращают результаты, позволяющие определить конец скопированной строки, в отличие от strcpy() и strcat(), которые (несколько бесполезно) возвращают указатель на начало места назначения.

2
Keith Thompson 25 Авг 2011 в 19:54

Вы не можете позволить себе игнорировать переполнение буфера; это один из основных способов сбоя веб-мира.

Учитывая показанную структуру данных, есть предел тому, что вы можете делать. Если бы структура данных включала длины каждой из строк в данных, переданных в функцию, вы могли бы сделать больше. Однако до тех пор вы должны определить длину каждой строки (и указать длину выходного буфера), а затем организовать безопасное копирование строк. Поскольку к моменту копирования вы будете знать длину строки, вы можете использовать memmove() или memcpy() для перемещения данных, и вы знаете длину, чтобы можно было настроить указатель:

int concatManyStrings(char *buffer, size_t buflen, const char **data, size_t nitems)
{
    assert(buflen > 0);
    char *dst = buffer;
    char *end = buffer + buflen - 1;
    for (size_t i = 0; i < nitems; i++)
    {
         size_t len = strlen(data[i]);
         if (dst + len >= end)
             return -1;
         memmove(dst, data[i], len);
         dst += len;
    }
    *dst = '\0';
    return 0;
}

Это сканирует каждую строку дважды - один раз для определения длины и один раз для копирования. Однако вы не можете позволить себе использовать strncpy() из-за его поведения с заполнением нулями (дьявольским в данном контексте); тот факт, что это не гарантирует нулевое завершение, не будет проблемой. Вы не можете использовать strcpy(), пока не узнаете, что длина безопасна, для чего требуется strlen(). Если данные были не простым массивом указателей на строки, а массивом структуры, включающей длину строки, а также указатель, то strlen() можно было бы избежать. С осторожностью можно использовать strcat() или strncat(); главное предостережение - избегать квадратичного поведения (алгоритм Шлемиля), которое может быть достигнуто, если вы определите конец каждой добавляемой строки. В случае strncat() будьте очень осторожны с параметром размера; он отличается от размера strncpy(). И вам все равно, вероятно, придется использовать strlen(), поскольку стандартные функции не сообщают о конце строки, где они поместили последний символ - что было бы намного полезнее, чем возврат указателя на первый символ целевой строки. .

Я не знаю стандартной функции для этого.

1
Jonathan Leffler 25 Авг 2011 в 19:53

Используйте snprintf, который, по сути, всегда является правильным ответом на любой вопрос о сборке строк:

snprintf(buf, buflen, "%s%s%s", str1, str2, str3);

К сожалению, это не работает для "произвольного n" в качестве счетчика входной строки; для этого просто напишите свой собственный цикл for ...

0
R.. GitHub STOP HELPING ICE 25 Авг 2011 в 20:12