关于相当于std :: string的c ++:strncpy吗?

strncpy equivalent for std::string?

C ++标准库中是否有与strncpy完全相同的东西?我的意思是一个函数,它将一个缓冲区中的字符串复制到另一个缓冲区中,直到命中终止0为止?例如,当我不得不从不安全的来源(例如TCP数据包)中解析字符串时,我可以在处理数据的同时进行长度检查。

我已经搜索了很多有关该主题的内容,还找到了一些有趣的主题,但是所有这些人都对std :: string :: assign感到满意,该函数还可以将字符大小复制为参数。我对该函数的问题是,它不执行任何检查是否已命中了终止null-它会认真对待给定的大小并像memcpy一样将数据复制到字符串缓冲区中。如果应对时进行了这种检查,则这种方式分配和复制的内存比必须完成的要多得多。

这就是我目前正在解决此问题的方式,但是我希望避免一些开销:

1
2
3
4
5
6
7
8
9
10
11
12
13
    // 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-binaries解析器的一部分(准确地说,是解析出口表的例程)。 rva2sra将相对虚拟地址转换为PE部分相对地址。 ExportDirectory_t结构包含RVA到二进制文件的导出名称,该名称应为零终止的字符串。但这并非总是如此-如果有人愿意,它将能够省略终止零,这将使我的程序运行到不属于该部分的内存中,从而最终导致崩溃(最好的情况下...)。

我自己实现这样的功能不是什么大问题,但是如果在C ++标准库中实现了该功能的解决方案,我更喜欢它。


如果知道要创建string的缓冲区至少包含一个NUL,则可以将其传递给构造函数:

1
2
3
4
5
const char[] buffer ="hello\\0there";

std::string s(buffer);

// s contains"hello"

如果不确定,则只需在字符串中搜索第一个空值,然后告诉string的构造函数复制那么多数据:

1
2
3
4
5
6
7
8
9
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,也总是可以运行)包装成一个简洁的小函数:

1
2
3
4
5
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);
}

但需要注意的一件事是:如果您将单参数构造函数本身交给const char*,它将一直运行到找到NUL。重要的是,如果使用std::string的单参数构造函数,则知道缓冲区中至少有一个NUL。

不幸的是(或者幸运的是)对于std::string没有内建的strncpy完美等效项。


Is there an exact equivalent to strncpy in the C++ Standard Library?

我当然希望不会!

I mean a function, that copies a string from one buffer to another until it hits the terminating 0?

嗯,但这不是strncpy()的作用-或至少不是它的全部作用。

strncpy()可让您指定目标缓冲区的大小n,最多可复制n个字符。就目前而言还可以。如果源字符串的长度(定义为终止'\\0'前面的字符数的"长度")超过n,则目标缓冲区将被附加的\\0'填充,这很少有用。并且如果源字符串的长度超过n,则不会复制终止的'\\0'

strncpy()函数是为早期Unix系统将文件名存储在目录条目中的方式而设计的:作为14字节固定大小的缓冲区,最多可容纳14个字符的名称。 (编辑:我不是100%确信这是其设计的真正动机。)可以说,它不是字符串函数,并且不仅仅是strcpy()的"更安全"变体。

您可以使用strncat()达到与strncpy()所做的假设相同的名称(给定名称):

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

这将始终'\\0'终止目标缓冲区,并且不会不必要地用额外的'\\0'字节填充它。

您是否真的在寻找与之等效的std::string

编辑:写完以上内容之后,我在博客上发布了这句话。


STL中的std::string类可以在字符串中包含空字符("xxx\\0yyy"是长度为7的完全有效的字符串)。这意味着它对空终止一无所知(几乎,有从/到C字符串的转换)。换句话说,strncpy的STL中没有其他选择。

有几种方法可以用较短的代码来实现您的目标:

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

要么

1
2
3
4
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];

字符串的子字符串构造函数可以执行您想要的操作,尽管它与strncpy并不完全等效(请参阅最后的说明):

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

Constructs the string with a substring [pos, pos+count) of other. If count == npos or if the requested substring lasts past the end of the string, the resulting substring is [pos, size()).

来源:http://www.cplusplus.com/reference/string/string/string/

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
#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;
}

输出:

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

与strncpy的区别:

  • 字符串构造函数总是在结果后附加一个以零结尾的字符,而strncpy则不会。
  • 如果在请求的计数之前到达了以空终止的字符,则字符串构造函数不会将结果填充为0,strncpy会这样做。

没有内置的等效项。您必须滚动自己的strncpy

1
2
3
4
5
6
7
8
9
10
11
12
#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));
}

std :: string具有可以使用的下一个签名的构造函数:

1
string ( const char * s, size_t n );

下一个说明:

Content is initialized to a copy of the string formed by the first n characters in the array of characters pointed by s.


使用类的构造函数:

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

根据此文档,这将产生准确的副本:http://www.cplusplus.com/reference/string/string/string/