我想为C风格的函数strftime编写一个方便的包装器。我已经找到了将char-array转换为字符串的一些选项,反之亦然。这是我的代码:
std::string Time::getAsFormattedString ( const std::string& format , const size_t& maxStringSize = 999 )
{
char* timeArray = 0;
std::string timeString;
// [OPTION_0]
timeArray = reinterpret_cast <char*> (malloc(sizeof(char)*maxStringSize)));
// [OPTION_1]
timeArray = const_cast <char*> (timeString.c_str());
// [OPTION_2]
timeArray = &(*(timeString.begin()));
strftime(timeArray,maxStringSize,format.c_str(),&this->time);
timeString = timeArray;
// [OPTION_0]
free(timeArray);
return timeString;
}
№0选项看起来很安全,因为在释放内存之前不能抛出任何异常(编辑:timeString = timeArray
可以抛出一个,在该行周围需要try-catch)
№1const-casting总是看起来像黑客
№2似乎是最好的我不知道是否可能存在一些问题
你能否告诉我,哪一个是最安全,最正确,最佳,也许是最佳实践。
谢谢。
答案 0 :(得分:3)
您提出的任何选项都不是真的可以接受;该 第二个和第三个甚至不会工作。在全球范围内,有两个 “可接受的”解决方案。
最简单的是:
char buffer[ 1000 ];
size_t n = strftime( buffer, sizeof( buffer ), format.c_str(), &time );
if ( n == 0 ) {
throw SomeError; // or you might just abort...
}
return std::string( buffer );
这具有简单的优点,但您必须这样做 将最大大小记录为界面中的约束。 (这对我来说似乎是一个合理的限制。)
或者,您可以删除约束:
std::vector<char> buffer( 100 );
size_t n = strftime( &buffer[0], buffer.size(), format.c_str(), &time );
while ( n == 0 ) {
buffer.resize( 2 * buffer.size() );
n = strftime( &buffer[0], buffer.size(), format.c_str(), &time );
}
return std::string( buffer.begin(), buffer.begin() + n );
(在C ++ 11中,在C ++ 03的实践中,您可以使用
直接std::string
,而不是std::vector
。其中
例如,您需要在结果字符串上调用resize( n )
在返回之前。)
答案 1 :(得分:2)
首先,唯一不会崩溃的选项是选项0。其他人会崩溃,因为你说你已经分配了999个字节,但事实上内部字符串可能只分配了1个字节,并且会发生悲伤的事情。
但是我可能会在这里通过在堆栈上分配一大块字符来做到这一点。
char timeArray[2048];
strftime(timeArray,2048,format.c_str(),&this->time);
return string(timeArray);
这样您就不必进行任何投射或动态分配,几乎肯定会更整洁,更快。
答案 2 :(得分:1)
选项2(或更好,&timeString[0]
)应该是首选。
你对选项1中的const_cast
不好是正确的,而在选项0中,你至少可以使用new
而不是{{来清理代码。 1}}(并避免演员表)
但更喜欢选项2。
(哦,正如评论者指出的那样,如果你正在写字符串本身,你显然必须首先调整它的大小,以至于你不会写出超出界限。鉴于支持者,我应该可能已经明确表示了这一点)
答案 3 :(得分:1)
选项1和2不好,因为您无意改变从std::string::c_str()
得到的字符串(c代表constant
)。选项2将需要“调整大小”字符串才能使用它。但我不确定字符串是否可以保证从同一缓冲区中复制...
我的解决方案是:
char timeArray[1000];
(虽然这样做太过分了。除非你多次重复相同的格式说明符,否则它不可能超过100个字符,而且非常冗长 - 所以“理智”的组合不会达到1000个字符以内的任何地方。 )
请注意timeString = timeArray
可以为bad_alloc
抛出异常,所以如果你不想在那种情况下泄漏内存,你需要使用基于堆栈的存储(如我的建议),智能指针或代码部分周围的try / catch块。
答案 4 :(得分:1)
唯一可行的解决方案是选项0(尽管可以执行一些小的调整)。正如其他人所指出的那样,该标准表示c_str()
方法返回的空间并不是用于写入目的,实际上它是一种允许std::string
被读取的方法。 C标准库(它是C ++标准库的一部分)。
标准中写道:
返回:指向长度大小为()的数组的初始元素的指针 + 1,其第一个size()元素等于由* this控制的字符串的相应元素,其最后一个元素为null charT()指定的字符。
需要:程序不得 更改存储在数组中的任何值。该计划也不应该 在任何后续之后将返回值视为有效指针值 调用类basic_string的非const成员函数 指定与此相同的对象。
所以,我只是快速修复你的代码:
const char * Time::getAsFormattedString(const std::string& format)
{
static char timeArray[256];
std::strftime( timeArray, 256, format.c_str(), &this->time );
return timeArray;
}
这使得方法的缓冲区可以在程序启动时创建,并且可以连续重用,因此不会出现内存错误(因为堆不受影响)。
唯一的问题是堆栈中有空间来创建你将存储函数结果的字符串,但无论如何这将在调用函数后发生,函数本身不会触及堆,并且只有最小的堆栈。
实际上,该功能的用处不受影响,因为从const char *
到std::string
的自动转换,您可以通常的方式安全地调用它:
std::string strTime = time.getAsFormattedString( "%F %T" );
希望这有帮助。