零终止字符串的 C++ LPSTR 和字符串问题
Posted
技术标签:
【中文标题】零终止字符串的 C++ LPSTR 和字符串问题【英文标题】:C++ LPSTR and string trouble with zero-terminated strings 【发布时间】:2015-12-10 11:53:12 【问题描述】:我正在使用来自Winapi
的GetOpenFileName
函数,并且我正在将过滤器应用于选择文件对话框。
这完美运行:
LPSTR mfilter = "Filter\0*.PDF\0";
ofn.lpstrFilter = mfilter;
if(GetOpenFileName(&ofn))
...
THIS 失败(对话框打开但未应用过滤器):
string mfilter = "Filter\0*.PDF\0";
ofn.lpstrFilter = mfilter.c_str();
if(GetOpenFileName(&ofn))
...
我需要使用std:string
,因为我通过参数获取文件扩展名,这种类型有助于连接,但我遇到了不兼容问题...
如果它按预期工作,这将是我的代码(失败与前面的示例相同):
const char * ext = &(4:); //Ampersand parameter (from CA Plex) It contains "PDF"
string mfilter = "Filter\0*." + ext + "\0"; //Final string: Filter\0*.PDF\0;
ofn.lpstrFilter = mfilter.c_str();
当我使用这个方法时,我得到了运行时异常:
string mf;
mf.append("Filter")
.append('\0')
.append("*.pdf")
.append('\0');
ofn.lpstrFilter = mf.c_str();
【问题讨论】:
如果您设置断点并检查 mfilter 缓冲区,您会看到什么? (崩溃前...) 每当我必须使用 MFC/WinAPI 时,我总是使用 MSism 方式。与之抗争是不值得的。 如果你不显示minimal reproducible example,我怀疑这会不会有成效。我猜你修改了字符串,或者销毁了它,从而使指针无效。 事实证明是:***.com/questions/34223930/… 【参考方案1】:有
string mfilter = "Filter\0*.PDF\0";
您正在调用 std::string
构造函数,它在第一个 \0
处终止字符串。
以下代码:
string mfilter = "Filter\0*.PDF\0";
cout << "string:" << mfilter << " len: " << mfilter.length() << endl;
打印
string: Filter len: 6
字符串仅在第一个 \0
终止符之前构造。难道字符串只由单词“Filter”组成。
【讨论】:
添加示例。谢谢。std::string
中嵌入 NUL 的使用是否已定义?如果事实证明你真的不能用std::string
做到这一点,(在下面回答你的问题)你将需要使用良好的老式内存分配和strcpy()
...
@andlabs 我不知道你说我需要定义“嵌入式 NUL 的使用”是什么意思
您不需要定义任何东西,C++ 标准可以。我发现的一切都表明是的,您可以将 NUL 与std::string
一起使用。您需要显示完整代码或使用调试器。
我无法使用调试器,因为我正在使用一个不允许它的 IDE(是的,难以置信)。我不介意展示我的完整代码,但问题出在我所说的地方,当试图对那个 Winapi 函数形成一个“过滤器”时,它不理解它并且做它想做的任何事情,这在 Windows API 中很常见。 【参考方案2】:
GetOpenFileName
函数使用 TCHAR,如果使用 UNICODE 字符集,TCHAR 将变为 WCHAR。
这是一个例子:
std::wstring getOpenFileName(HWND hWnd, const std::wstring& sFilter)
wchar_t buffer[MAX_PATH] = L"";
OPENFILENAMEW ofn = 0;
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hWnd;
ofn.lpstrFilter = sFilter.c_str();
ofn.nFilterIndex = 1;
ofn.lpstrFile = buffer;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST;
if( !::GetOpenFileNameW( &ofn ) )
return L"";
return buffer;
如果您想基于std::wstring
参数化lpstrFilter
,您可以使用wstring::c_str()
来获得LPCTSTR
,在UNICODE 的情况下为const wchar*
。
重要提示:问题在于采用const wchar*
的std::wstring
构造函数假定输入是C 字符串。 C 字符串以 '\0' 终止,因此在到达 '\0' 字符时停止解析。为了弥补这一点,您需要使用 构造函数,该构造函数接受两个参数,一个指向 char 数组的指针和一个长度。
您也可以使用string::push_back()
方法附加NULL。
std::wstring sFilter = L"PDF Files";
sFilter.push_back('\0');
sFilter.append(L"*.pdf");
sFilter.push_back('\0');
【讨论】:
我唯一遇到的问题是动态设置ofn.lpstrFilter
。文件扩展名是通过参数传递的,我需要在对话框打开时将该扩展名过滤器应用于对话框。正如我所看到的,您将过滤器设置为文字字符串,这就是第一个示例显示的对我有用的方式,但这不是解决方案。您将如何使用始终保持空终止符的参数动态设置过滤器字符串?谢谢。
这不是答案。从问题中的工作示例可以明显看出,OP 仅使用 ANSI 函数。
这并不明显等。在 2015 年使用 ASCII 字符集而不使用 UNICODE 是一种犯罪。
我已经更新了答案来描述参数化 lpstrFilter 的方式
Asker 出现运行时错误。无论 ANSI 在 2015 年是否构成犯罪,您都需要解决实际问题。【参考方案3】:
string mfilter = "Filter\0*.PDF\0";
这会调用一个std::basic_string
构造函数,该构造函数使用一个以空字符结尾的字符串。它将停止解析 "Filter"
处的字符串文字。
试试这个:
string mfilter( "Filter\0*.PDF", 13 ); // need double null at end
这调用了一个std::basic_string
构造函数,它使用“s指向的字符串的第一个count个字符。s可以包含空字符。”
如果您经常遇到此问题,您必须自己计算字符数,或者编写包装代码。
相关:std::basic_string
constructors.
至于你的运行时错误:
string mf;
mf.append("Filter")
.append('\0')
.append("*.pdf")
.append('\0');
append()
does not have an overload for a single character type。您可能遇到了const CharT* s
重载,带有一个空指针。
使用append( 1, '\0' )
或append( "", 1 )
,其中任何一个都应附加一个空字节。
【讨论】:
string mfilter( "Filter\0*.PDF", 12 );
需要改为 string mfilter( "Filter\0*.PDF\0", 13 );
,因为 GetOpenFileName()
期望过滤器以 double 为空终止。 c_str()
将为您添加一个空终止符,但数据需要包含另一个空终止符。
@RemyLebeau:到目前为止,我很高兴能够在我的职业生涯中很好远离 WinAPI。 ;) 感谢您的提醒。但是由于字符串文字末尾带有一个隐含的零,因此我不必加一,只需增加计数即可。 (或者c_str()
是否跳过添加呢?)
计数 12 不包括隐式 null。无论最终的 null 是隐式还是显式,您都必须将计数增加到 13。 c_str()
总是在 std::string
实际持有的任何内容的末尾包含自己的 null。以上是关于零终止字符串的 C++ LPSTR 和字符串问题的主要内容,如果未能解决你的问题,请参考以下文章