C ++标准库中是否存在与 strncpy 等效的完全?我的意思是一个函数,它将一个字符串从一个缓冲区复制到另一个缓冲区,直到它到达终止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 ++标准库中实现了这个功能,我更喜欢它。
答案 0 :(得分:11)
如果您知道要创建string
的缓冲区中至少有一个NUL,那么您可以将其传递给构造函数:
const char[] buffer = "hello\0there";
std::string s(buffer);
// s contains "hello"
如果您不确定,那么您只需要在字符串中搜索第一个null,并告诉string
的构造函数复制那么多数据:< / p>
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);
}
但有一点需要注意:如果单独使用单参数构造函数const char*
,它将一直运行直到找到NUL。如果您使用std::string
的单参数构造函数,则必须知道缓冲区中至少有一个NUL。
不幸的是(或者幸运的是)strncpy
没有完全相同的std::string
。
答案 1 :(得分:3)
STL中的std::string
类可以在字符串中包含空字符("xxx\0yyy"
是一个长度为7的完全有效的字符串)。这意味着它不知道有关空终止的任何信息(差不多,有来自/到C字符串的转换)。换句话说,strncpy
的STL中没有替代方案。
有一些方法可以通过更短的代码来实现目标:
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];
答案 2 :(得分:3)
C ++标准库中是否存在与strncpy等效的完全?
我当然希望不会!
我的意思是一个函数,它将一个字符串从一个缓冲区复制到另一个缓冲区,直到它到达终止0?
啊,但那不是strncpy()
所做的 - 或者至少它不是所有。
strncpy()
允许您指定目标缓冲区的大小n
,并复制最多n
个字符。就目前而言,这很好。如果源字符串的长度(“长度”定义为终止'\0'
之前的字符数)超过n
,则目标缓冲区将填充额外的\0'
s,这是很少见的有用。如果源字符串的长度超过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
的那个吗?
编辑:在我写完上述内容后,我在我的博客上发布了this rant。
答案 3 :(得分:1)
std::string有一个构造函数,可以使用下一个签名:
string ( const char * s, size_t n );
有下一个描述:
内容初始化为由s指向的字符数组中前n个字符组成的字符串副本。
答案 4 :(得分:1)
字符串的子串构造函数可以做你想要的,尽管它不是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的区别:
答案 5 :(得分:0)
使用类'构造函数:
string::string str1("Hello world!");
string::string str2(str1);
根据此文档,这将产生一个完整的副本:http://www.cplusplus.com/reference/string/string/string/
答案 6 :(得分:0)
没有内置的等价物。你必须推出自己的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));
}