問題的出現: 我在VS上用c C++寫的跨平臺的函數 html
移植到Qt 上面 出現sprintf_s 函數格式化出錯。python
開始覺得是編碼問題 反覆查找Qt亂碼問題 。個人編譯文件編碼utf8 編譯器minGW 32 默認編碼應該也是utf8 照常說沒錯。c++
查了不少關於文件編碼 執行編碼的問題編程
https://www.cnblogs.com/liunian1004/p/5912536.html文章 很好的介紹了QSting 編碼處理問題。ubuntu
解決:windows
常常多方嘗試和詢問 後面發現 sprintf函數格式化沒問題,我靠,怎麼會這樣。數組
後面查找了下 發現sprintf_s 是windows平臺的函數。ruby
qt中查看sprintf_s 頭文件(f2查看),函數
此刻大概理解多是由於sprintf_s 調用了系統的dll 把格式化字符串用本地字符集處理。ui
本地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\n", "赤紅", 11);
亦或者用類型無關的流運算符
std::ostringstream os; os << "赤紅" << "'s age is " << 11 << "\n"; std::string s = os.str();
暫且不談效率問題,這種用<<拼接多個不一樣類型對象的作法代碼量較大,並且在控制具體輸出格式時更爲麻煩,好比控制數字所佔位數,或者小數點後位數。至少繁雜得讓我老是記不起來,寧肯使用C風格snprintf來控制。好比
double d = 3.1415926; snprintf(buf, BUFFSIZE, "圓周率: %-8.3lf是祖沖之發現的\n", 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 << "是祖沖之發現的\n";
除了代碼如此之長以及有可能漏掉std::fixed外,還有問題在於setprecision已經改變了默認設置,也就是說,若是再os <<傳入一個浮點數,保留的小數點位數仍然是3位。
也許有人說,這種好處在於setprecision和setw接收的能夠是一個變量而很是量。實際上snprintf同樣能夠作到。
double d = 3.1415926; int n1 = 8, n2 = 3; snprintf(buf, BUFFSIZE, "圓周率: %-*.*lf是祖沖之發現的\n", n1, n2, d);
在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++; // 算上終止符'\0' 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++; // 算上終止符'\0' 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) + "\n"; 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