Я хочу скопировать строку в массив символов и не превысить буфер.
Таким образом, если у меня есть массив символов размера 5, затем я хочу скопировать максимум 5 байтов от строки в него.
что код должен сделать это?
Прежде всего, strncpy
почти наверняка не то, что вам нужно. strncpy
был разработан для довольно конкретной цели. Он находится в стандартной библиотеке почти исключительно потому, что он уже существует, а не потому, что он обычно полезен.
Вероятно, самый простой способ сделать то, что вы хотите, - это примерно так:
sprintf(buffer, "%.4s", your_string.c_str());
В отличие от strncpy
, это гарантирует, что результат будет завершен NUL, но не заполняет дополнительные данные в цели, если исходный код короче указанного (хотя последнее не является серьезной проблемой, когда целевая длина равна 5).
Некоторые хорошие версии libc предоставляют нестандартную, но отличную замену для strcpy (3)
/ strncpy (3)
- strlcpy (3)
.
Если ваш нет, исходный код находится в свободном доступе здесь из репозитория OpenBSD .
std::string str = "Your string";
char buffer[5];
strncpy(buffer, str.c_str(), sizeof(buffer));
buffer[sizeof(buffer)-1] = '\0';
Последняя строка обязательна, потому что strncpy не гарантирует завершение строки NUL (вчера было обсуждение мотивации).
Если вы использовали широкие строки, вместо sizeof (buffer)
вы использовали бы sizeof (buffer) / sizeof (* buffer)
или, что еще лучше, макрос вроде
#define ARRSIZE(arr) (sizeof(arr)/sizeof(*(arr)))
/* ... */
buffer[ARRSIZE(buffer)-1]='\0';
Это именно то, что делает функция копирования std :: string
.
#include <string>
#include <iostream>
int main()
{
char test[5];
std::string str( "Hello, world" );
str.copy(test, 5);
std::cout.write(test, 5);
std::cout.put('\n');
return 0;
}
Если вам нужно нулевое завершение, вы должны сделать что-то вроде этого:
str.copy(test, 4);
test[4] = '\0';
Используйте функцию неработающую ссылку и материал, не найденный на целевом сайте , если ваша реализация предоставляет такую ссылку (функция отсутствует в стандартной библиотеке C), но это довольно широко принятое де-факто стандартное имя для "безопасной" функции копирования ограниченной длины для строк с нулевым завершением. strlcpy
Если ваша реализация не предоставляет функцию strlcpy
, реализуйте ее самостоятельно. Например, что-то подобное может сработать для вас
char *my_strlcpy(char *dst, const char *src, size_t n)
{
assert(dst != NULL && src != NULL);
if (n > 0)
{
char *pd;
const char *ps;
for (--n, pd = dst, ps = src; n > 0 && *ps != '\0'; --n, ++pd, ++ps)
*pd = *ps;
*pd = '\0';
}
return dst;
}
(на самом деле, de-facto accept strlcpy
возвращает size_t
], поэтому вы можете предпочесть реализовать принятую спецификацию вместо того, что я сделал выше).
Остерегайтесь ответов, в которых рекомендуется использовать strncpy
для этой цели. strncpy
не является безопасной функцией копирования строк ограниченной длины и не предполагается использовать для этой цели. Хотя вы можете заставить strncpy
«работать» для этой цели, это все равно похоже на закручивание деревянных шурупов молотком.
Обновление: Думал, что попытаюсь связать воедино некоторые ответы, ответы, которые убедили меня в том, что моя собственная первоначальная реакция на коленный рефлекс был плохим.
Во-первых, как заметил AndreyT в комментариях к этому вопросу, методы усечения (snprintf, strlcpy и strncpy) часто не являются хорошим решением. Часто лучше сравнить размер строки string.size ()
с длиной буфера и вернуть / выдать ошибку или изменить размер буфера.
Если усечение в вашей ситуации подходит, IMHO, strlcpy - лучшее решение, поскольку это самый быстрый / минимальный накладной метод, обеспечивающий завершение с нулевым значением. К сожалению, это не во многих / всех стандартных дистрибутивах и поэтому не переносимо. Если вы делаете много этого, возможно, стоит предоставить свою собственную реализацию, AndreyT привел пример . Он выполняется в O ( длина результата ). Также справочная спецификация возвращает количество скопированных байтов, что может помочь определить, был ли источник усечен.
Другими хорошими решениями являются sprintf и snprintf . Они являются стандартными, поэтому переносимы и обеспечивают безопасный результат с нулевым завершением.У них больше накладных расходов, чем у strlcpy (анализ спецификатора строки формата и списка переменных аргументов), но если вы не выполняете много из них, вы, вероятно, не заметите разницы. Он также выполняется в O ( длина результата ). snprintf всегда безопасен, и этот sprintf может переполниться, если вы неправильно указали спецификатор формата (как отмечали другие, строка формата должна быть "%.
не "%
). Эти методы также возвращают количество скопированных байтов.
Частным случаем является strncpy . Он выполняется в O ( длина буфера ), потому что, если он достигает конца src, он обнуляет остаток буфера. Полезно только в том случае, если вам нужно обнулить хвост буфера или вы уверены, что длины целевой и исходной строк совпадают. Также обратите внимание, что это небезопасно, поскольку не обязательно завершать строку нулем. Если источник усечен, то значение NULL не будет добавлено, поэтому вызовите последовательно с присвоением NULL, чтобы гарантировать завершение NULL: strncpy (buffer, str.c_str (), BUFFER_LAST); buffer [BUFFER_LAST] = '\ 0';
std::string my_string("something");
char* my_char_array = new char[5];
strncpy(my_char_array, my_string.c_str(), 4);
my_char_array[4] = '\0'; // my_char_array contains "some"
С помощью strncpy
вы можете скопировать не более n символов из источника в место назначения. Однако обратите внимание, что если длина исходной строки не превышает n символов, адрес назначения не будет завершаться нулевым символом; вы должны сами поместить в него завершающий нулевой символ.
Массив символов длиной 5 может содержать не более строки из 4 символов, поскольку пятый должен быть завершающим нулевым символом. Следовательно, в приведенном выше коде n = 4.
Если у вас всегда есть буфер размером 5, вы можете сделать:
std::string s = "Your string";
char buffer[5]={s[0],s[1],s[2],s[3],'\0'};
Изменить: Конечно, при условии, что ваш std :: string достаточно велик.