Как скопировать строку в массив символов в C++, не пробегаясь через буфер

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

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

что код должен сделать это?

21
задан neuromancer 22 May 2010 в 19:11
поделиться

8 ответов

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

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

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

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

21
ответ дан 29 November 2019 в 06:19
поделиться

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

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

3
ответ дан 29 November 2019 в 06:19
поделиться
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';
0
ответ дан 29 November 2019 в 06:19
поделиться

Это именно то, что делает функция копирования 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';
42
ответ дан 29 November 2019 в 06:19
поделиться

Используйте функцию 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;
}

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

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

7
ответ дан 29 November 2019 в 06:19
поделиться

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

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

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

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

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

3
ответ дан 29 November 2019 в 06:19
поделиться
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.

0
ответ дан 29 November 2019 в 06:19
поделиться

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

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

Изменить: Конечно, при условии, что ваш std :: string достаточно велик.

-2
ответ дан 29 November 2019 в 06:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: