Как скопировать строку в массив символов в C ++, не переходя через буфер - PullRequest
21 голосов
/ 22 мая 2010

Я хочу скопировать строку в массив char, а не переполнить буфер.

Поэтому, если у меня есть массив char размера 5, я хочу скопировать максимум 5 байтов из строкив это.

какой код для этого?

Ответы [ 11 ]

39 голосов
/ 23 мая 2010

Это именно то, что делает функция копирования 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';
20 голосов
/ 22 мая 2010

Прежде всего, strncpy почти наверняка не , что вы хотите. strncpy был разработан для довольно конкретной цели. Он находится в стандартной библиотеке почти исключительно потому, что он уже существует, а не потому, что он обычно полезен.

Вероятно, самый простой способ сделать то, что вы хотите, это что-то вроде:

sprintf(buffer, "%.4s", your_string.c_str());

В отличие от strncpy, это гарантирует, что результат будет завершен NUL, но не заполняет дополнительные данные в цели, если источник короче указанного (хотя последний не является основной проблемой, когда длина цели 5).

7 голосов
/ 22 мая 2010

Используйте функцию strlcpy неработающая ссылка и материал не найден на сайте назначения , если ваша реализация предоставляет его (функция отсутствует в стандартном C библиотека), однако оно довольно широко признано в качестве фактического стандартного имени для «безопасной» функции копирования ограниченной длины для строк с нулевым символом в конце.

Если ваша реализация не обеспечивает функцию 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;
}

(На самом деле принятый де-факто strlcpy возвращает size_t, поэтому вы можете предпочесть реализовать принятую спецификацию вместо того, что я делал выше).

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

3 голосов
/ 22 мая 2010

Некоторые хорошие версии libc предоставляют нестандартную, но отличную замену strcpy(3) / strncpy(3) - strlcpy(3).

Если у вас нет, исходный код находится в свободном доступе здесь из репозитория OpenBSD .

3 голосов
/ 22 мая 2010

Обновление: Думаю, что я попытаюсь связать воедино некоторые ответы, ответы, которые убедили меня в том, что мой собственный оригинальный ответ на коленный удар был плохим.

Во-первых, как отметил АндрейТ в комментариях к этому вопросу, методы усечения (snprintf, strlcpy и strncpy) часто не являются хорошим решением. Часто лучше проверять размер строки string.size() по длине буфера и возвращать / выдавать ошибку или изменять размер буфера.

Если в вашей ситуации с усечением все в порядке, ИМХО, strlcpy - лучшее решение, являющееся самым быстрым / с наименьшими издержками методом, обеспечивающим нулевое завершение. К сожалению, это не во многих / всех стандартных дистрибутивах и поэтому не переносимо. Если вы делаете много из них, возможно, стоит предоставить собственную реализацию, AndreyT привел пример . Он работает в O ( длина результата ). Также эталонная спецификация возвращает количество скопированных байтов, что может помочь в обнаружении усеченного источника.

Другими хорошими решениями являются sprintf и snprintf . Они являются стандартными и поэтому являются переносимыми и обеспечивают безопасный нулевой завершенный результат. Они имеют больше накладных расходов, чем strlcpy (анализ спецификатора строки формата и списка переменных переменных), но если вы не выполняете много из них, вы, вероятно, не заметите разницу. Он также работает в O ( длина результата ). snprintf всегда безопасен, и этот sprintf может переполниться, если вы неправильно укажете спецификатор формата (как уже отмечали другие, строка формата должна быть "%.<N>s", а не "%<N>s"). Эти методы также возвращают количество скопированных байтов.

Особый вариант решения: strncpy . Он работает в O ( длина буфера ), потому что, если он достигает конца src, он обнуляет оставшуюся часть буфера. Полезно, только если вам нужно обнулить хвост буфера или вы уверены, что длина строки назначения и исходной строки совпадают. Также обратите внимание, что это небезопасно в том смысле, что не обязательно завершать строку нулем. Если источник обрезан, то значение null добавляться не будет, поэтому вызовите последовательность с нулевым присваиванием, чтобы гарантировать завершение нуля: strncpy(buffer, str.c_str(), BUFFER_LAST); buffer[BUFFER_LAST] = '\0';

2 голосов
/ 26 февраля 2013
void stringChange(string var){

    char strArray[100];
    strcpy(strArray, var.c_str()); 

}

Полагаю, это должно сработать. он скопирует строку формы в массив символов.

1 голос
/ 23 декабря 2012

я думаю, что snprintf () намного безопаснее и проще

snprintf ( buffer, 100, "The half of %d is %d", 60, 60/2 );

Нулевой символ добавляется и заканчивается автоматически:)

0 голосов
/ 27 июня 2012
char mystring[101]; // a 100 character string plus terminator
char *any_input;
any_input = "Example";
iterate = 0;
while ( any_input[iterate] != '\0' && iterate < 100) {
    mystring[iterate] = any_input[iterate];
    iterate++;
}
mystring[iterate] = '\0';

Это базовый эффективный дизайн.

0 голосов
/ 23 мая 2010

Если у вас всегда есть буфер размером 5, то вы можете сделать:

std::string s = "Your string";
char buffer[5]={s[0],s[1],s[2],s[3],'\0'};

Редактировать: Конечно, предполагая, что ваш std :: string достаточно большой.

0 голосов
/ 22 мая 2010
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';
...