簡體   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