【问题标题】:How to get a writable C buffer from std::string?如何从 std::string 获取可写的 C 缓冲区?
【发布时间】:2015-09-06 19:11:26
【问题描述】:

我正在尝试将我的代码从使用 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 覆盖上面的方法?

【问题讨论】:

  • 。如果需要修改字符串,请修改字符串。如果您将字符串传递给采用常量 char 指针的旧函数,则只需要“C 缓冲区”。
  • @JoachimPileborg:好的,让我们假设CharLower API 不是CharLower,而是一些任意的API,它将修改我需要从std::string 获取的输入缓冲区。我该怎么做?这就是我要问的。
  • 我告诉你,你不需要原始缓冲区,你需要的一切都已经在字符串类或标准库中。看看例如en.cppreference.com/w/cpp 并在那里浏览一会儿。
  • @JoachimPileborg 所以你的意思是,你应该总是从头开始,而不是更新旧代码......?
  • 我的意思是,修改std::string 对象所需的所有代码已经存在,无论是在std::string 类本身中,还是通过使用标准库中的其他函数..您根本不必重写或从头开始任何东西,因为代码已经在那里供您使用。大小写转换、修改子字符串、追加、前置、插入,一切都为你准备好了。

标签: c++ windows mfc stdstring


【解决方案1】:

在我的新工作中,我们不使用 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:\TESTING\toBeCutOff)?") );
::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 \0 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:\TESTING\toBeCutOff)?") );
::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 \0 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 \0 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 \0 character
path.shrink_to_fit();                  // optional to adjust the string's
                                       // capacity - useful if you
                                       // do not plan to modify the string again

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

std::string 方法与 CString 方法相比的优势在于,您不必声明额外的 C-String 指针变量,您只需使用官方的 std::string 方法和一个 @987654332 @/wcslen 函数。 我上面显示的方法仅适用于结果 Win32-API 缓冲区为空终止时的缩小变体,但对于 Win32-API 返回未终止字符串的非常特殊的情况,然后 - 类似于 CString::ReleaseBuffer 方法 - 你必须通过path.resize( newLength ) 明确知道并指定新的字符串/缓冲区长度——就像path.ReleaseBuffer( newLength )CString 替代方案一样。

【讨论】:

    【解决方案2】:
    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;
    }
    

    如您所见,您可以使用“old school c-pointer”既可以将字符串输入到旧函数中,也可以将其用作 OUT 参数。当然,您需要确保字符串中有足够的容量使其能够工作等。

    【讨论】:

    • 是否保证是可能的,还是您的标准库实现的怪癖?
    • 您似乎可以互换使用“容量”和“大小”,请注意这一点,因为在 C++ 库中,“容量”和“大小”意味着两个不同的东西。
    • 是的,我本可以更好地写容量/尺寸的东西。从函数 GetString() 的角度来看,调用方 std::string 的大小就是容量。 ;)
    • @immibis 是的,&s[0] 甚至在 std::string 定义成员(例如data())之前就已经存在,并且一直以这种方式工作。
    • @BitTickler 在 C 和 C++ 中,“它总是这样工作”是不够的。
    【解决方案3】:

    根据您的要求,您可以使用以下一项或多项:

    1. std::string::operator[]()。此函数返回给定索引处的字符,不进行边界检查。

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

    3. std::string::data()。该函数返回一个指向原始数据的const 指针。

    4. std::string::c_str()。该函数返回与std::string::data()相同的值

    【讨论】:

    • 谢谢。是的,我看到我可以得到一个指向字符串的const 指针,但是我不能修改它,可以吗?否则不会是const,对吗?
    • 没错,不能通过data()c_str()返回的指针修改字符串。
    • 嗯。有趣的。人们告诉我std::string 比 MFC 的CString 好得多。不能做最简单的操作后不知道是不是真的。
    • 如果获取一个指向原始数据的可写指针是判断两者的唯一标准,那么std::string就没有那么好了。但是,如果您将要求提高一点,但仍然无法使用 std::string 完成您可以使用 CString 完成的事情,那么我们正在谈论真正的问题。请记住,您可以使用前两个函数访问原始数据的内容。
    • 你看我正在修改现有的代码。所以我有点受限于已经写好的内容。
    【解决方案4】:

    std::string 小写的公认方法是:

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

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

    如果您需要为标准“C”语言环境以外的语言环境获取toLower(),则可以使用:

    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);这是设计使然,会破坏使用std::string 的某些动机。

    【讨论】:

    • 请不要偏离轨道。我不是要转换为小写。这只是我快速整理的一个示例。我问的是从 std::string 获取可写缓冲区。
    • @c00000fd 我相信,这与您问题中的代码一样多。字符串类和标准库拥有你真正需要的一切。
    • 是的,我可以得到const char *,但我怎样才能得到char *呢?我需要自己分配它并在那里复制我的字符串。不会浪费CPU周期吗?
    • @c00000fd 对于std::string 中包含的数据,只允许使用常量缓冲区。修改此缓冲区将导致std::string 中的未定义行为。这一切都是设计使然。如果std::string 的功能对您来说不重要,并且限制对您来说过于局限,那么您不应该使用std::string
    • @SpecialSauce:顺便说一句,因为您提出了降低字符串大小写的“更好”方法。只是让你知道。我刚刚用std::wstring 和一串非英文字符尝试了你建议的方法,但它没有改变这些的大小写。所以请注意这一点。
    猜你喜欢
    • 2017-01-05
    • 1970-01-01
    • 2011-03-16
    • 1970-01-01
    • 2013-06-16
    • 1970-01-01
    • 2014-11-28
    • 2013-06-24
    • 1970-01-01
    相关资源
    最近更新 更多