如何从std::string中获取可写的C缓冲区

How to get a writable C buffer from std::string?

本文关键字:缓冲区 获取 std string      更新时间:2023-10-16

我正在尝试将我的代码从使用MFC的CString移植到适用于Microsoft Windows平台的std::string。我对一些事情很好奇。例如:

CString MakeLowerString(LPCTSTR pStr)
{
    CString strLower = pStr ? pStr : L"";
    CharLower(strLower.GetBuffer());        //Use WinAPI
    strLower.ReleaseBuffer();
    return strLower;
}

我使用strLower.GetBuffer()来获得一个可写缓冲区,以便传递给CharLower API。但我在std::string中没有看到类似的方法。

我是不是错过了什么?如果是,您将如何使用std::string覆盖上面的方法?

在我的新工作中,我们不使用MFC,但幸运的是std-lib和C++11,所以我遇到了与c00000fd相同的问题。多亏了BitTickler的回答,我想出了通过&s[0] resp为Win32 API使用字符串的内部缓冲区的想法。&s.front()捕获。

使用收缩内部字符串缓冲区的Win32-API函数

假设您有一个字符串,该字符串将被Win32-API函数(例如::PathRemoveFileSpec(path))缩短,您可以遵循以下方法:

std::string path( R("?(C:TESTINGtoBeCutOff)?") );
::PathRemoveFileSpec( &path.front() ); // Using the Win32-API
                                       // and the the string's internal buffer
path.resize( strlen( path.data() ) );  // adjust the string's length 
                                       // to the first  character
path.shrink_to_fit();                  // optional to adjust the string's
                                       // capacity - useful if you
                                       // do not plan to modify the string again

Unicode版本:

std::wstring path( LR("?(C:TESTINGtoBeCutOff)?") );
::PathRemoveFileSpec( &path.front() ); // Using the Win32-API
                                       // and the the string's internal buffer
path.resize( wcslen( path.data() ) );  // adjust the string's length 
                                       // to the first  character
path.shrink_to_fit();                  // optional to adjust the string's
                                       // capacity - useful if you
                                       // do not plan to modify the string again

使用扩展内部字符串缓冲区的Win32-API函数

假设你有一个字符串,它将被Win32-API函数扩展或填充,例如::GetModuleFileName(NULL, path, cPath)来检索你的可执行文件的路径,你可以遵循这种方法:

std::string path;
path.resize(MAX_PATH);                 // adjust the internal buffer's size
                                       // to the expected (max) size of the
                                       // output-buffer of the Win32-API function
::GetModuleFileName( NULL, &path.front(), static_cast<DWORD>( path.size() ) );
                                       // Using the Win32-API
                                       // and the the string's internal buffer
path.resize( strlen( path.data() ) );  // adjust the string's length 
                                       // to the first  character
path.shrink_to_fit();                  // optional to adjust the string's
                                       // capacity - useful if you
                                       // do not plan to modify the string again

Unicode版本:

std::wstring path;
path.resize(MAX_PATH);                 // adjust the internal buffer's size
                                       // to the expected (max) size of the
                                       // output-buffer of the Win32-API function
::GetModuleFileName( NULL, &path.front(), static_cast<DWORD>( path.size() ) );
                                       // Using the Win32-API
                                       // and the the string's internal buffer
path.resize( wcslen( path.data() ) );  // adjust the string's length 
                                       // to the first  character
path.shrink_to_fit();                  // optional to adjust the string's
                                       // capacity - useful if you
                                       // do not plan to modify the string again

当你最终收缩以适应字符串时,与MFC替代方案相比,在扩展字符串的内部缓冲区时,你只需要多一行代码,而在收缩字符串时,它的开销几乎相同。

CString方法相比,std::string方法的优势在于,您不必声明额外的C字符串指针变量,只需使用官方的std::string方法和一个strlen/wcslen函数即可。上面所示的方法仅适用于Win32-API缓冲区为null终止时的收缩变体,但对于Win32-API返回未终止字符串的特殊情况,则-类似于CString::ReleaseBuffer方法-您必须通过path.resize( newLength )显式知道并指定新的字符串/缓冲区长度-就像CString备选方案的path.ReleaseBuffer( newLength )一样。

void GetString(char * s, size_t capacity)
{
    if (nullptr != s && capacity > 5)
    {
        strcpy_s(s,capacity, "Hello");
    }
}
void FooBar()
{
    std::string ss;
    ss.resize(6);
    GetString(&ss[0], ss.size());
    std::cout << "The message is:" << ss.c_str() << std::endl;
}

正如您所看到的,您既可以使用"老派c-pointer"将字符串输入到遗留函数中,也可以将其用作OUT参数。当然,您需要确保字符串中有足够的容量使其工作等。

根据您的需求,您可以使用以下一个或多个:

  1. CCD_ 18。此函数返回给定索引处的字符,不进行边界检查。

  2. std::string::at()。此函数通过边界检查返回给定索引处的字符。

  3. CCD_ 20。此函数返回一个指向原始数据的const指针。

  4. CCD_ 22。此函数返回与std::string::data() 相同的值

要使仅包含ASCII字符的std::string小写,可以使用以下代码:

#include <algorithm>
#include <string> 
std::string data = "Abc"; 
std::transform(data.begin(), data.end(), data.begin(), ::tolower);

你真的无法绕过每个角色的迭代。最初的Windows API调用将在内部执行相同的字符迭代。

如果您需要获得用于多字节编码(例如UTF-8)的toLower(),或者除标准";C";区域设置,您可以使用:

std::string str = "Locale-specific string";
std::locale loc("en_US.UTF8");  // desired locale goes here
const ctype<char>& ct = use_facet<ctype<char> >(loc);
std::transform(str.begin(), str.end(), str.begin(), std::bind1st(std::mem_fun(&ctype<char>::tolower), &ct));

要直接回答您的问题并减去任何上下文,您可以调用str.c_str()std::string中获取const char *(LPCSTR)。不能直接将std::string转换为char *(LPTSTR);这是故意的,会破坏使用CCD_ 31的一些动机。