// 輸入範例
"-0.92,-0.60,341.77,-0.15,0.14,0.11,0.02,-0.01,1.00,1.00,447.19,140.02,159.50"
目標:按 , 分割 → 轉為 float/double → 存入陣列。
strtok + strtod(推薦)#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_NUM 100
int parse_csv(const char *input, double *out, int max) {
char *buf = strdup(input); // strtok 會修改字串,需複製
if (!buf) return -1;
int count = 0;
char *token = strtok(buf, ",");
while (token && count < max) {
char *endptr;
double val = strtod(token, &endptr);
if (endptr == token) { // 無有效數字
free(buf);
return -1;
}
out[count++] = val;
token = strtok(NULL, ",");
}
free(buf);
return count;
}
int main() {
const char *data = "-0.92,-0.60,341.77,-0.15,0.14,0.11,0.02,-0.01,1.00,1.00,447.19,140.02,159.50";
double nums[MAX_NUM];
int n = parse_csv(data, nums, MAX_NUM);
if (n > 0) {
for (int i = 0; i < n; i++) printf("[%d] %.5f\n", i, nums[i]);
}
return 0;
}
int manual_parse(const char *s, double *out, int max) {
int count = 0;
while (*s && count < max) {
// 跳過空白
while (*s == ' ') s++;
int sign = 1;
if (*s == '-') { sign = -1; s++; }
else if (*s == '+') { s++; }
double val = 0.0;
int has_digit = 0;
// 整數部份
while (*s >= '0' && *s <= '9') {
val = val * 10 + (*s - '0');
s++; has_digit = 1;
}
// 小數部份
if (*s == '.') {
s++;
double frac = 0.1;
while (*s >= '0' && *s <= '9') {
val += (*s - '0') * frac;
frac *= 0.1;
s++; has_digit = 1;
}
}
if (!has_digit) break; // 非數字字元
out[count++] = sign * val;
// 跳過分隔符
while (*s == ' ') s++;
if (*s == ',') s++;
else break;
}
return count;
}
| 函數 | 優點 | 缺點 |
|---|---|---|
strtok | 標準、簡潔 | 非執行緒安全、修改原字串、不支援空欄位 |
strtok_r / strtok_s | 執行緒安全 | C11/C17 才標準化,Windows 用 strtok_s |
strtod | 健壯處理科學計數法、十六進制、錯誤報告 | 略重 |
sscanf | 格式化強大 | 效能差、緩衝區風險、難處理變長欄位 |
| 手寫解析 | 零依賴、完全可控、教學價值高 | 易出邊界錯誤、需自行處理 inf/nan |
strtod**,配合 strtok_r 或自寫分割迭代器。endptr** 確保整個 token 被消費,防止 "123abc" 偷偷通過。mmap + 狀態機流式解析,避免全量複製。strtod 受 LC_NUMERIC 影響(小數點可能為 ,),跨平台建議固定 C locale 或手寫解析。