zoukankan      html  css  js  c++  java
  • Qt sprintf_s函数格式化字符串出错

    Qt sprintf_s函数格式化字符串出错

    问题的出现: 我在VS上用c C++写的跨平台的函数 

    移植到Qt 上面 出现sprintf_s 函数格式化出错。

    开始以为是编码问题  反复查找Qt乱码问题  。我的编译文件编码utf8  编译器minGW 32 默认编码应该也是utf8  照常说没错。

    查了很多关于文件编码 执行编码的问题

    https://www.cnblogs.com/liunian1004/p/5912536.html文章 很好的介绍了QSting 编码处理问题。

    解决:

    经常多方尝试和询问  后面发现 sprintf函数格式化没问题,我靠,怎么会这样。

    后面查找了下 发现sprintf_s 是windows平台的函数。

    qt中查看sprintf_s 头文件(f2查看),

    此刻大概理解可能是因为sprintf_s 调用了系统的dll 把格式化字符串用本地字符集处理。

    本地gdb 执行编码是utf8  所以格式化错误。

    编码就是地雷 是坑。所有系统统一用utf8多理想,多美好。

     字符串格式化

    结果: 字符串用snprintf 格式化简单方便

    注意在vs平台没有snprintf 函数 需要如下处理

    #if _MSC_VER
    #define snprintf _snprintf
    #endif

    转:https://www.jianshu.com/p/548e43f4ced8 文件介绍的很好

    两种格式化字符串方法

    众所周知,C++的std::string功能残缺,各种功能都没有,比如格式化字符串功能。
    在python3中,支持两种格式化字符串的方法,一种是C风格,格式化的部分用%开头,%后面的对应具体类型(比如%s对应字符串%d对应整型),另一种则是类型无关的风格,{0}对应第1个参数,{1}对应第2个参数。

    >>> "{0}'s age is {1}".format("赤红", 11)
    "赤红's age is 11"
    >>> "%s's age is %d" % ("赤红", 11)
    "赤红's age is 11"
    

    而在C++中则只能借用C函数,用snprintf来格式化一片缓冲区

    #define BUFFSIZE 512
        char buf[BUFFSIZE];
        snprintf(buf, BUFFSIZE, "%s's age is %d
    ", "赤红", 11);
    

    亦或者用类型无关的流运算符

        std::ostringstream os;
        os << "赤红" << "'s age is " << 11 << "
    ";
        std::string s = os.str();
    

    暂且不谈效率问题,这种用<<拼接多个不同类型对象的做法代码量较大,而且在控制具体输出格式时更为麻烦,比如控制数字所占位数,或者小数点后位数。至少繁杂得让我总是记不起来,宁可使用C风格snprintf来控制。比如

        double d = 3.1415926;
        snprintf(buf, BUFFSIZE, "圆周率: %-8.3lf是祖冲之发现的
    ", d);
    
    $ ./a.out 
    圆周率: 3.142   是祖冲之发现的
    

    通过%-8.3lf将lf(long float即double)类型的浮点数设置占位数为8,设置小数点后位数为3,负号表示左对齐,这种表示方法非常简单紧凑。
    至于用C++的iomanip头文件实现,我还花了点时间查文档。

        double d = 3.1415926;
        os << "圆周率: " << std::setw(8) << std::fixed
           << std::setprecision(3) << std::left
           << d << "是祖冲之发现的
    ";
    

    除了代码如此之长以及有可能漏掉std::fixed外,还有问题在于setprecision已经改变了默认设置,也就是说,如果再os <<传入一个浮点数,保留的小数点位数仍然是3位。
    也许有人说,这种好处在于setprecision和setw接收的可以是一个变量而非常量。实际上snprintf一样可以做到。

        double d = 3.1415926;
        int n1 = 8, n2 = 3;
        snprintf(buf, BUFFSIZE, "圆周率: %-*.*lf是祖冲之发现的
    ", n1, n2, d);
    

    C++包装snprintf生成格式化的std::string对象

    APUE UNP TLPI这几本讲Linux下C编程的书中,都自己写了错误处理库来包装snprintf产生格式化的输出,以免每次重复定义缓冲区/调用snprintf等等。
    这样的做法有个缺陷就是缓冲区(字符数组)长度有限制,当然一般而言buffer size定义得足够大的话是足够的,毕竟打印太长的格式化字符串不如多调用几次函数。
    另一方面,由于这些函数仅仅是打印信息,尤其是经常打印信息后直接退出程序。所以不会返回错误字符串。如果在C++中想要把错误信息作为异常传给上一层处理,这些函数是不够的。因此需要简单修改下。

    inline std::string format_string(const char* format, va_list args) {
        constexpr size_t oldlen = BUFSIZ;
        char buffer[oldlen];  // 默认栈上的缓冲区
        va_list argscopy;
        va_copy(argscopy, args);
        size_t newlen = vsnprintf(&buffer[0], oldlen, format, args) + 1;
        newlen++;  // 算上终止符''
        if (newlen > oldlen) {  // 默认缓冲区不够大,从堆上分配
            std::vector<char> newbuffer(newlen);
            vsnprintf(newbuffer.data(), newlen, format, argscopy);
            return newbuffer.data();
        }
        return buffer;
    }
    
    inline std::string format_string(const char* format, ...) {
        va_list args;
        va_start(args, format);
        auto s = format_string(format, args);
        va_end(args);
    
        return s;
    }
    

    这是模仿UNP的实现,定义形参为va_list和...的两个版本,其中接受va_list的版本还可为其它函数所用。因为C风格的可变参数列表...不能作为参数传递。另一点,va_list类型也不一定有拷贝构造函数,因此得用va_copy来拷贝一份va_list,以供第二次使用。
    C++11新增了可变模板参数特性,使得上述代码可以得到简化

    template <typename ...Args>
    inline std::string format_string(const char* format, Args... args) {
        constexpr size_t oldlen = BUFSIZ;
        char buffer[oldlen];  // 默认栈上的缓冲区
    
        size_t newlen = snprintf(&buffer[0], oldlen, format, args...);
        newlen++;  // 算上终止符''
    
        if (newlen > oldlen) {  // 默认缓冲区不够大,从堆上分配
            std::vector<char> newbuffer(newlen);
            snprintf(newbuffer.data(), newlen, format, args...);
            return std::string(newbuffer.data());
        }
    
        return buffer;
    }
    

    而传递可变模板参数也变得十分容易(使用forward完美转发),示例代码如下

    xyz@ubuntu:~/unp_practice/lib$ cat test.cc 
    #include <string.h>
    #include <unistd.h>
    #include "format_string.h"
    
    template <typename ...Args>
    void errExit(const char* format, Args... args) {
        auto errmsg = format_string(format, std::forward<Args>(args)...);
        errmsg = errmsg + ": " + strerror(errno) + "
    ";
        fputs(errmsg.c_str(), stderr);
        exit(1);
    }
    
    int main() {
        const char* s = "hello world!";
        int fd = -1;
        if (write(fd, s, strlen(s)) == -1)
            errExit("write "%s" to file descriptor(%d) failed", s, fd);
        return 0;
    }
    xyz@ubuntu:~/unp_practice/lib$ g++ test.cc -std=c++11
    xyz@ubuntu:~/unp_practice/lib$ ./a.out 
    write "hello world!" to file descriptor(-1) failed: Bad file descriptor
  • 相关阅读:
    积水路面Wet Road Materials 2.3
    门控时钟问题
    饮料机问题
    Codeforces Round #340 (Div. 2) E. XOR and Favorite Number (莫队)
    Educational Codeforces Round 82 (Rated for Div. 2)部分题解
    Educational Codeforces Round 86 (Rated for Div. 2)部分题解
    Grakn Forces 2020部分题解
    2020 年百度之星·程序设计大赛
    POJ Nearest Common Ancestors (RMQ+树上dfs序求LCA)
    算法竞赛进阶指南 聚会 (LCA)
  • 原文地址:https://www.cnblogs.com/swing07/p/10422088.html
Copyright © 2011-2022 走看看