目录
问题提出
问题分析
解决方案
拓展疑问:format函数为什么没有对string做适配
拓展解答:format不是一个泛型函数
我在使用cv::String::format()方法时候对string英文的格式化输出返回乱码
对英文字符常量输出正常(OpenCV3.4.7)
string imgname;
string imgname1;
string imgPath;
string imgPath1;
imgname = "DSC02930.JPG";
imgname1 = "DSC02931.JPG";
imgPath = format("G:\\%s", "DSC02930.JPG");//对英文字符常量输出正常
cout << imgname <
输出结果:
求教原因和解决方案
没人理我,唉,自己继续尝试吧
已经知道字符串常量能够正常输出,先来看看char数组的输出
char str[] = "HHHH";
imgPath1 = format("G:\\%s", str);
显然,char数组的输出是没有任何问题的
那为什么std::string和cv::String类型的对象不能正常输出呢??
我开始怀疑
1)难道是直接使用string对象的时候,传递给函数format的是对象的地址?而非其中字符数组的地址?
或者说在函数体内并没有从对象本身做提取得到字符数组的操作?
2)直接使用string对象的时候,传递给函数format的参数默认编码为Unicode等多字节编码?
查看相关调用函数链之后,觉得无从验证,因为我不了解更底层的方法的使用,比如va_list、va_start、vsnprintf
不过从这些已有代码来看的话,应该确实没有从对象本身做提取得到字符数组(即猜想1)
再加上每次得到的乱码都不相同,所以基本可以确认猜想1,否定猜想2。
String format( const char* fmt, ... ){ // core/src/system.cpp
AutoBuffer buf;
for ( ; ; ){
va_list va;
va_start(va, fmt);
int bsize = static_cast(buf.size());
int len = cv_vsnprintf(buf.data(), bsize, fmt, va);
va_end(va);
CV_Assert(len >= 0 && "Check format string for errors");
if (len >= bsize){
buf.resize(len + 1);
continue;
}
buf[bsize - 1] = 0;
return String(buf.data(), len);
}
}
int cv_vsnprintf(char* buf, int len, const char* fmt, va_list args) { // core/src/system.cpp
#if defined _MSC_VER
if (len <= 0) return len == 0 ? 1024 : -1;
int res = _vsnprintf_s(buf, len, _TRUNCATE, fmt, args);
// ensure null terminating on VS
if (res >= 0 && res < len){
buf[res] = 0;
return res;
}
else{
buf[len - 1] = 0; // truncate happened
return res >= len ? res : (len * 2);
}
#else
return vsnprintf(buf, len, fmt, args);
#endif
}
int vsnprintf (char * __restrict__ __stream, size_t __n, const char * __restrict__ __format, va_list __local_argv){ // stdio.h
return __ms_vsnprintf (__stream, __n, __format, __local_argv);
}
那么现在我们就想办法从string对象中提取得到关键数据(即字符数组char[]),两种方法
imgname.data());
查看一下它的源码,直接调用并返回的 (this->_Get_data()._Myptr());
_NODISCARD _Ret_z_ const _Elem * data() const noexcept
{ // return pointer to immutable array
return (this->_Get_data()._Myptr());
}
imgname.c_str();
查看一下它的源码,同样是直接调用并返回的 (this->_Get_data()._Myptr());
_NODISCARD _Ret_z_ const _Elem * c_str() const noexcept
{ // return pointer to null-terminated immutable array
return (this->_Get_data()._Myptr());
}
二者除了注释中多了个null-terminated修饰词,以及函数名不同,其他完全一样
奇奇怪怪
虽然现在看来出现这个问题并花费这么多时间显得有点低级
但我还是有点不能理解,既然format函数能够接受string类型对象,为什么会没有对string做适配呢?
其实从format函数的源码就能看出来,这不是一个泛型函数,其底层机制的与printf的机制完全一样,并没有对参数列表中的对象进行分析。只要是个地址就行,于是导致了错误
比如我们随便定义一个对象,使用format做%s的输出:
vector vm;
imgPath1 = format("G:\\%s", vm);
cout << imgname1 << endl;
cout << imgPath1 << endl;
得到结果: