在C+中有没有更优雅的方式将sprintf和std::string结合在一起+;? 我的C++代码中经常使用以下类型的帮助函数: static inline std::string stringf(const char*fmt,…) { std::字符串ret; //对付瓦拉格人 va_列表参数; va_启动(参数、fmt); //根据参数调整字符串大小 ret.resize(vsnprintf(0,0,fmt,args)); //结束varargs并重新启动,因为vsnprintf破坏了我们的args va_端(args); va_启动(参数、fmt); //填补空缺 如果(!ret.empty()) { vsnprintf(&ret.front(),ret.size()+1,fmt,args); } //可变段结束 va_端(args); //返回字符串 返回ret; }

在C+中有没有更优雅的方式将sprintf和std::string结合在一起+;? 我的C++代码中经常使用以下类型的帮助函数: static inline std::string stringf(const char*fmt,…) { std::字符串ret; //对付瓦拉格人 va_列表参数; va_启动(参数、fmt); //根据参数调整字符串大小 ret.resize(vsnprintf(0,0,fmt,args)); //结束varargs并重新启动,因为vsnprintf破坏了我们的args va_端(args); va_启动(参数、fmt); //填补空缺 如果(!ret.empty()) { vsnprintf(&ret.front(),ret.size()+1,fmt,args); } //可变段结束 va_端(args); //返回字符串 返回ret; },c++,string,c++11,printf,string-formatting,C++,String,C++11,Printf,String Formatting,它有几个优点: 字符串长度没有任意限制 字符串就地生成,不会被复制(如果RVO正常工作) 没有来自外界的惊喜 现在,我有几个问题: 瓦拉格家的重新扫描有点难看 std::string在内部是一个连续的字符串,后面直接有空终止符,这一事实在规范中似乎没有明确说明。这是通过->c_str()必须是O(1)并返回一个空终止字符串而隐含的,我相信&(data()[0])应该等于&(*begin()) vsnprintf()被调用两次,第一次可能会执行昂贵的一次性工作 有人知道更好的方法吗?你(双关语)嫁

它有几个优点:

  • 字符串长度没有任意限制
  • 字符串就地生成,不会被复制(如果RVO正常工作)
  • 没有来自外界的惊喜
  • 现在,我有几个问题:

  • 瓦拉格家的重新扫描有点难看
  • std::string在内部是一个连续的字符串,后面直接有空终止符,这一事实在规范中似乎没有明确说明。这是通过->c_str()必须是O(1)并返回一个空终止字符串而隐含的,我相信&(data()[0])应该等于&(*begin())
  • vsnprintf()被调用两次,第一次可能会执行昂贵的一次性工作
  • 有人知道更好的方法吗?

    你(双关语)嫁给了
    std::sprintf()
    ?如果使用C++和OHSO,代码< STD::String ,为什么不充分利用新的语言特性,使用VALIDAY模板来做一个类型安全的代码> SCAFTF < /COD> >返回一个<代码> STD::String ?p>
    检查这个非常漂亮的实现:。我认为这解决了您的所有问题。

    不要使用大小为0的第一个
    vsnprintf
    。相反,使用可能大小的堆栈缓冲区(例如,64到4096之间的某个值),如果合适,则将其复制到返回值中

    您对
    std::string
    的连续性的担心是错误的。它定义明确,完全可以信赖


    最后,我想重申编译时格式检查库会更好。您需要C++14才能获得全部功能,但C++11支持的功能足够多,您应该能够
    #ifdef
    一些头代码,而不会丢失任何表现力。不过,记住要考虑
    gettext

    当您添加标记
    c++11
    时,我想您可以使用它。然后,您可以将代码简化为:

    namespace fmt {
    
    template< class ...Args >
    std::string sprintf( const char * f, Args && ...args ) {
        int size = snprintf( nullptr, 0, f, args... );
        std::string res;
        res.resize( size );
        snprintf( & res[ 0 ], size + 1, f, args... );
        return res;
    }
    
    }
    
    int main() {
        cout << fmt::sprintf( "%s %d %.1f\n", "Hello", 42, 33.22 );
        return 0;
    }
    
    名称空间fmt{
    模板<类…参数>
    std::string sprintf(const char*f,Args&&…Args){
    int size=snprintf(nullptr,0,f,args…);
    std::string res;
    res.resize(大小);
    snprintf(&res[0],大小+1,f,args…);
    返回res;
    }
    }
    int main(){
    
    cout
    boost::format
    就是为了这个目的而存在的,那么引入一个新配偶怎么样:
    ostringstream
    ?可能相关:另外@ThomasMatthews
    ostringstream
    对于国际化是无用的,而且丑陋/缓慢。在共享实用程序代码中隐藏一些可能的大小会导致灾难。将它放在堆栈上这也可能不是一个好主意——因为在调试为什么有人“%s”打印50MB字符串的当天,您肯定会遇到至少两个额外的问题。@YuriyRomanenko什么?这不是灾难,它的大小是固定的。任何大小,包括0,都是有效的(0大小的数组是扩展除外)对于第一轮。单元测试一个比大小小一个大的字符串是很容易的。合理的值范围是64到4096。我不明白。我可以在内存中尽可能多地进行sprintf,我可以制作一个std::string,只要我有内存,为什么我要将我的函数限制为4096字节?@Yuriyromanko:你保留动态的逻辑再次分配和打印,这允许几乎不受限制的输出长度,但如果第一次适合的话可以跳过。这是一种优化,而不是限制。哦,我明白了,这听起来确实像是性能上的改进——假设sprintf到NULL几乎总是比MemcPyth慢,但这并不是更优雅或简单引擎盖下的e,但外观明显优越!谢谢!