Конкатенация строк C за линейное время с crt - PullRequest
1 голос
/ 25 августа 2011

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

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

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

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

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

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

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

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

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

Ответы [ 3 ]

2 голосов
/ 25 августа 2011

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

1 голос
/ 25 августа 2011

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

Учитывая указанную структуру данных, существует предел того, что вы можете сделать.Если бы структура данных включала длины каждой из строк в данных, передаваемых в функцию, вы могли бы сделать больше.Однако до тех пор вы должны определить длину каждой строки (и указать длину выходного буфера), а затем организовать безопасное копирование строк.Поскольку к моменту копирования вы будете знать длину строки, вы можете использовать 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(), так как стандартные функции не сообщают о конце строки, где они поместили последний символ - что было бы гораздо полезнее, чем возвращение указателя на первый символ целевой строки.

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

0 голосов
/ 26 августа 2011

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

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

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

...