繁体   English   中英

stn :: string的strncpy等价物?

[英]strncpy equivalent for std::string?

在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节相对地址。 structure contains the RVA to the export name of the binary, which should be a zero-terminated string. 结构包含二进制文件的导出名称的RVA,该名称为零终止字符串。 但事情并非总是如此 - 如果有人想要它,它将能够省略终止零,这将使我的程序运行到不属于该部分的内存中,它最终会崩溃(在最好的情况下...)。

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

如果你知道你想要创建一个string的缓冲区中至少有一个NUL,那么你可以将它传递给构造函数:

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

std::string s(buffer);

// s contains "hello"

如果您不确定 ,那么您只需要在字符串中搜索第一个null,并告诉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);
}

但有一点需要注意: 如果你将单参数构造const char*单独设置为一个const char* ,它将一直运行直到它找到一个NUL。 重要的是,如果使用std::string的单参数构造函数,则知道缓冲区中至少有一个NUL

不幸的是(或幸运的是), std::string没有内置完美的strncpy等价物。

STL中的std::string类可以在字符串中包含空字符( "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];

在C ++标准库中是否与strncpy 完全等效?

我当然希望不是!

我的意思是一个函数,它将一个字符串从一个缓冲区复制到另一个缓冲区,直到它到达终止0?

啊,但这不是strncpy()所做的 - 或者至少它不是全部

strncpy()允许您指定目标缓冲区的大小n ,并最多复制n字符。 就目前而言,这很好。 如果源字符串的长度(“长度”定义为终止'\\0'之前的字符数)超过n ,则目标缓冲区将填充额外的\\0' ,这很少有用。 如果源字符串的长度超过n ,则不会复制终止'\\0'

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

您可以使用strncat()实现与strncpy()所做的相同的操作(给定名称strncat()

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

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

你真的在寻找与之相当的std::string吗?

编辑:在我写完上述内容之后,我在我的博客上发布了这个咆哮

字符串的子串构造函数可以做你想要的,尽管它不是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)之前达到空终止字符,则字符串构造函数不会将结果填充为0。

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

string ( const char * s, size_t n );

有下一个描述:

内容被初始化为由s指向的字符数组中的前n个字符形成的字符串的副本。

没有内置的等价物。 你必须滚动你自己的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));
}

使用类的构造函数:

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM