首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C 語言 sprintf 與 sscanf 函數完全指南:格式化輸入輸出深度解析

     編輯:關於C語言

前言

在 C/C++ 開發中,字串格式化處理是核心基本功。sprintf 與 sscanf 作為標準庫中最強大的格式化工具,雖然用法看似簡單,但細節決定成敗。本文將從原理、常見用法、進階技巧到避坑指南,系統梳理這兩個函數的精髓。

一、sprintf:構建字串的瑞士軍刀

1.1 函數原型與核心機制

int sprintf(char *buffer, const char *format, ...);

sprintf 是變參函數,前兩個參數固定:目標緩衝區與格式化字串。其精華在於格式化字串中的 % 佔位符,配合後續變參列表,將各類型資料「拼裝」成字串。與 printf 唯一不同:前者輸出到字串,後者輸出到標準輸出。

1.2 整數格式化:從基礎到進階

基本輸出與寬度控制

sprintf(s, "%d", 123);           // "123"
sprintf(s, "%8d%8d", 123, 4567); // "     123    4567" 右對齊補空格
sprintf(s, "%-8d%8d", 123, 4567); // "123     4567" 左對齊

進制轉換與零填充

sprintf(s, "%8x", 4567);    // 小寫十六進制,寬度 8,右對齊
sprintf(s, "%-8X", 4568);   // 大寫十六進制,寬度 8,左對齊
sprintf(s, "%08X", 4567);   // "000011D7" 寬度 8,前導補零(關鍵技巧)

符號擴展陷阱與對策

變參函數無類型安全,short 會被整型提升為 int。若直接打印 short si = -1 配合 %04X,會得到 FFFFFFFF 而非預期的 FFFF。

short si = -1;
sprintf(s, "%04X", (unsigned short)si); // 正確:強制零擴展
// 或直接使用無符號類型
unsigned short usi = -1;
sprintf(s, "%04X", usi);

1.3 浮點數精準控制

sprintf(s, "%f", 3.1415926);     // "3.141593" 預設 6 位小數
sprintf(s, "%10.3f", 3.1415626); // "     3.142" 總寬 10,小數 3 位
sprintf(s, "%-10.3f", 3.1415626); // "3.142     " 左對齊
sprintf(s, "%.3f", 3.1415626);   // "3.142" 僅限制小數位

血淚教訓:絕不可用 %f 對應整數變數,會導致位模式誤讀,結果不可預測。必須顯式轉型 (double)i。

1.4 字串操作:超越 strcat 的靈活性

基本拼接

char *who = "I", *whom = "CSDN";
sprintf(s, "%s love %s.", who, whom); // "I love CSDN."

處理非結尾字串(指定長度)

若字元陣列無 \0 結尾,%s 會越界。解法:用 %.ns 限制最大讀取長度。

char a1[] = {'A','B','C','D','E','F','G'};
char a2[] = {'H','I','J','K','L','M','N'};
sprintf(s, "%.7s%.7s", a1, a2); // "ABCDEFGHIJKLMN" 安全截取

動態寬度/精度(* 佔位符)

sprintf(s, "%.*s%.*s", 7, a1, 7, a2);           // 等同 %.7s%.7s
sprintf(s, "%.*s%.*s", sizeof(a1), a1, sizeof(a2), a2); // 更通用
sprintf(s, "%-*d", 4, 'A');       // "65  " 動態寬度
sprintf(s, "%#0*X", 8, 128);      // "0X000080" 動態寬度+前綴
sprintf(s, "%*.*f", 10, 2, 3.1415926); // "      3.14" 動態總寬+小數位

1.5 指標地址輸出

sprintf(s, "%p", &i); // 標準做法,自動適配架構(32/64 位)
// 等效於:sprintf(s, "%0*x", 2*sizeof(void*), &i);

1.6 善用返回值:避免重複 strlen

sprintf 返回實際寫入字元數(不含結尾 \0)。

int len = sprintf(s, "%d", i); // 直接獲得字串長度
// 實戰:高效拼接隨機數
char buf[64];
int offset = 0;
for (int i = 0; i < 10; i++) {
    offset += sprintf(buf + offset, "%d,", rand() % 100);
}
buf[offset - 1] = '\n'; // 替換最後逗號

此法比 strcat 高效:後者每次需掃描尋找 \0,前者直接記錄位置。

1.7 常見坑點速查表

  • 緩衝區溢出:給足夠大空間,字串輸入務必用 %.ns 限長。
  • 漏掉第一個參數:慣用 printf 易犯的低級錯誤。
  • 變參對應錯位:格式符與實參數量、類型必須嚴格對應,特別是 * 佔位的動態參數。

二、sscanf:從字串解析資料的利器

2.1 函數原型與返回值

int sscanf(const char *str, const char *format, ...);

成功返回成功轉換的參數個數;失敗返回 EOF(-1);若首個轉換即失敗返回 0。

2.2 核心格式符:%[] 掃描集

比 %s 更強大,支援字元集合與排除集:

  • %[a-z]:僅讀取小寫字母,遇其他字元停止。
  • %[^#]:讀取直到遇到 # 為止(不含 #)。
  • %20[^#]:最多讀 20 字元,遇 # 停止。

2.3 實戰範例:解析結構化日誌/資料行

範例 1:CSV 風格解析

char buf[] = "Notebook,IBM,ThinkPad X32,6,12000";
char category[21], name[21], vendor[21], sku[21];
float price; int quantity;
sscanf(buf, "%20[^,],%20[^,],%20[^,],%d,%f", 
       name, vendor, sku, &quantity, &price);

範例 2:自定義分隔符(#)

// 格式:name#sku#price#quantity#category#vendor
sscanf(buf, "%20[^#]#%20[^#]#%f#%d#%20[^#]#%20[^\n]",
       name, sku, &price, &quantity, category, vendor);

範例 3:複雜日誌解析

char log[] = "<14>2002-11-11 12:12:12 11.22.33.44 3 3 aaaa aaaaaa";
int a1, a5, a6; char str2[20], str3[20], str4[20], str7[20];
sscanf(log, "<%d>%s %s %s %d %d %s", &a1, str2, str3, str4, &a5, &a6, str7);

範例 4:跳過欄位(* 抑制賦值)

sscanf(input, "%d %x %5[a-z] %*s %f", &i, &j, s, s); 
// %*s 讀取並丟棄 "bbbbbbbb"

2.4 進階技巧:掃描集組合拳

  • 提取特定欄位:sscanf(str, "%[^ ] %[^ ] %[^$]", a, b, c); 以空格、$ 為界。
  • 處理變長欄位:結合寬度限制與排除集,健壯解析非固定格式字串。

三、姊妹函數與替代方案

  • strftime:專門格式化時間字串,用法類似 sprintf,但需指定緩衝區上限,更安全。
    time_t t = time(0);
    char s[32];
    strftime(s, sizeof(s), "%Y-%m-%d %H:%M:%S", localtime(&t));
  • MFC 封裝:CString::Format、CTime::Format 提供物件導向介面,碼風更優雅。

四、總結

sprintf 與 sscanf 是 C 語言格式化 I/O 的雙子星。掌握格式字串語法(%[flags][width][.precision][length]specifier)、理解變參類型提升規則、善用動態寬度與掃描集,並時刻警惕緩衝區溢出與參數錯位,方能寫出健壯、高效的程式碼。實戰中,優先考慮 snprintf / sscanf_s 等安全版本,或使用 C++ std::format(C++20)等現代替代方案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved