strncpy эквивалент для std :: string? - PullRequest
7 голосов
/ 16 января 2012

Существует ли точный эквивалент strncpy в стандартной библиотеке C ++? Я имею в виду функцию, которая копирует строку из одного буфера в другой, пока не достигнет завершающего 0? Например, когда мне нужно проанализировать строки из небезопасного источника, такого как TCP-пакеты, я могу выполнять проверку длины при копировании данных.

Я уже много искал по этой теме, и я также нашел несколько интересных тем, но все эти люди были довольны std :: string :: assign, который также может принимать размер символов для копирования в качестве параметра. , Моя проблема с этой функцией заключается в том, что она не выполняет никаких проверок, если завершающий ноль уже был достигнут - он принимает данный размер серьезно и копирует данные так же, как memcpy сделает это в буфер строки. Таким образом, выделяется и копируется намного больше памяти, чем нужно было делать, если бы при копировании была такая проверка.

Именно так я сейчас работаю над этой проблемой, но есть некоторые издержки, которые я бы хотел избежать:

    // Get RVA of export name
    const ExportDirectory_t *pED = (const ExportDirectory_t*)rva2ptr(exportRVA);
    sSRA nameSra = rva2sra(pED->Name);

    // Copy it into my buffer
    char *szExportName = new char[nameSra.numBytesToSectionsEnd];
    strncpy(szExportName, 
            nameSra.pSection->pRawData->constPtr<char>(nameSra.offset),
            nameSra.numBytesToSectionsEnd);
    szExportName[nameSra.numBytesToSectionsEnd - 1] = 0;

    m_exportName = szExportName;
    delete [] szExportName;

Этот фрагмент кода является частью моего парсера для PE-двоичных файлов (точнее, процедуры синтаксического анализа таблицы экспорта). rva2sra преобразует относительный виртуальный адрес в относительный адрес PE-секции. Структура ExportDirectory_t содержит RVA для имени экспорта двоичного файла, который должен быть строкой с нулевым символом в конце. Но это не всегда должно быть так - если кому-то это понравится, он мог бы опустить завершающий ноль, из-за которого моя программа запустилась бы в память, которая не принадлежит разделу, где она, наконец, потерпела бы крах ( в лучшем случае ...).

Не было бы большой проблемой реализовать такую ​​функцию самостоятельно, но я бы предпочел, чтобы такое решение было реализовано в стандартной библиотеке C ++.

Ответы [ 7 ]

11 голосов
/ 16 января 2012

Если вы знаете, что в буфере, из которого вы хотите сделать string, есть хотя бы один NUL, вы можете просто передать его конструктору:

const char[] buffer = "hello\0there";

std::string s(buffer);

// s contains "hello"

Если выВы не уверены, , тогда вам просто нужно найти строку с первым нулем и сказать конструктору string, что нужно скопировать столько данных:

int len_of_buffer = something;
const char* buffer = somethingelse;

const char* copyupto = std::find(buffer, buffer + len_of_buffer, 0); // find the first NUL

std::string s(buffer, copyupto);

// s now contains all the characters up to the first NUL from buffer, or if there
// was no NUL, it contains the entire contents of buffer

Вы можете обернутьвторая версия (которая всегда работает, даже если в буфере нет NUL) превращается в аккуратную маленькую функцию:

std::string string_ncopy(const char* buffer, std::size_t buffer_size) {
    const char* copyupto = std::find(buffer, buffer + buffer_size, 0);

    return std::string(buffer, copyupto);
}

Но одна вещь, на которую следует обратить внимание: , если вы отдаете один-argument конструктор a const char* сам по себе, он будет идти, пока не найдет NUL.Важно, чтобы вы знали, что в буфере есть по крайней мере один NUL, если вы используете конструктор с одним аргументом std::string.

К сожалению (или к счастью), не существует встроенного идеального эквивалентаstrncpy для std::string.

3 голосов
/ 17 января 2012

Существует ли точный эквивалентный strncpy в стандартной библиотеке C ++?

Я, конечно, надеюсь, что нет!

Я имею в виду функцию, которая копирует строку из одного буфера в другой, пока не достигнет завершающего 0?

Ах, но это не то, что делает strncpy() - или, по крайней мере, это не все , что делает.

strncpy() позволяет вам указать размер, n, целевого буфера и копировать не более n символов. Это нормально, насколько это возможно. Если длина исходной строки («длина», определяемая как число символов, предшествующих завершающему '\0') превышает n, целевой буфер дополняется дополнительными \0' с, что редко бывает полезно. И если длина, если исходная строка превышает n, то окончание '\0' не копируется .

Функция strncpy() была разработана для того, чтобы ранние системы Unix хранили имена файлов в записях каталога: в виде 14-байтового буфера фиксированного размера, который может содержать до 14-символьного имени. (РЕДАКТИРОВАТЬ: Я не уверен на 100%, что было фактической мотивацией для его дизайна.) Возможно, это не строковая функция, и это не просто "более безопасный" вариант strcpy().

Вы можете получить эквивалент того, что можно предположить strncpy() (с именем), используя strncat():

char dest[SOME_SIZE];
dest[0] = '\0';
strncat(dest, source_string, SOME_SIZE);

Это всегда '\0' определяет конечный буфер и не будет напрасно заполнять его дополнительными '\0' байтами.

Вы действительно ищете std::string эквивалент этого?

РЕДАКТИРОВАТЬ: После того, как я написал выше, я разместил эту напыщенную речь в моем блоге.

3 голосов
/ 16 января 2012

Класс std::string в STL может содержать нулевые символы в строке ("xxx\0yyy" - абсолютно допустимая строка длины 7). Это означает, что он ничего не знает о нулевом завершении (ну, почти, есть преобразования из / в строки C). Другими словами, в STL нет альтернативы для strncpy.

Есть несколько способов достичь цели с помощью более короткого кода:

const char *ptr = nameSra.pSection->pRawData->constPtr<char>(nameSra.offset);
m_exportName.assign(ptr, strnlen(ptr, nameSra.numBytesToSectionsEnd));

или

const char *ptr = nameSra.pSection->pRawData->constPtr<char>(nameSra.offset);
m_exportName.reserve(nameSra.numBytesToSectionsEnd);
for (int i = 0; i < nameSra.numBytesToSectionsEnd && ptr[i]; i++) 
  m_exportName += ptr[i];
1 голос
/ 27 сентября 2016

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

std::string( const std::string& other, 
          size_type pos, 
          size_type count = std::string::npos,
          const Allocator& alloc = Allocator() );

Создает строку с подстрокой [pos, pos + count) из другого. Если count == npos или запрошенная подстрока длится после конца строки, результирующая подстрока имеет вид [pos, size ()).

Источник: http://www.cplusplus.com/reference/string/string/string/

Пример:

#include <iostream>
#include <string>
#include <cstring>
int main ()
{
    std::string s0 ("Initial string");
    std::string s1 (s0, 0, 40); // count is bigger than s0's length
    std::string s2 (40, 'a');   // the 'a' characters will be overwritten
    strncpy(&s2[0], s0.c_str(), s2.size());
    std::cout << "s1: '" << s1 << "' (size=" << s1.size() << ")" << std::endl;
    std::cout << "s2: '" << s2 << "' (size=" << s2.size() << ")" << std::endl;
    return 0;
}

Выход:

s1: 'Initial string' (size=14)
s2: 'Initial string' (size=40)

Отличия от strncpy:

  • строковый конструктор всегда добавляет к результату символ, заканчивающийся нулем, strncpy - нет;
  • строковый конструктор не дополняет результат нулями, если до запрошенного числа достигнут нулевой завершающий символ, это делает strncpy.
1 голос
/ 16 января 2012

std :: string имеет конструктор со следующей подписью, которую можно использовать:

string ( const char * s, size_t n );

со следующим описанием:

Содержимое инициализировано вкопия строки, образованной первыми n символами в массиве символов, на которые указывает s.

0 голосов
/ 06 августа 2014

Нет встроенного аналога. Вы должны бросить свой собственный strncpy.

#include <cstring>
#include <string>

std::string strncpy(const char* str, const size_t n)
{
    if (str == NULL || n == 0)
    {
        return std::string();
    }

    return std::string(str, std::min(std::strlen(str), n));
}
0 голосов
/ 16 января 2012

Используйте конструктор класса:

string::string str1("Hello world!");
string::string str2(str1);

Это даст точную копию, согласно этой документации: http://www.cplusplus.com/reference/string/string/string/

...