對 C 語言新手 FAQ 裡面,應建議學習 sprintf 與 sscanf 之學習與使用。這兩支與 printf、scanf 極為相似,只是 printf、scanf 是從標準輸出入做 輸出、輸入動作。而 sprintf 與 sscanf 只是將目的改為字串而已。

舉個常見的例子,要產生 1.txt ~ 100.txt 之字串,該如何產生?

char filename[200];
for(int i=1; i<=100; ++i)
    sprintf(filename, "%d.txt", i);

 

類似的,要產生 A001.txt ~ A100.txt 之字串,該如何產生? 

char filename[200];
for(int i=1; i<=100; ++i)
    sprintf(filename, "A%03d.txt", i);

 

接下來問題就是對於 printf 熟練度而已。相對的,如果拿到一字串為 "1 2 3",怎麼拆解成三個數字? 

int a, b, c;
char buf[200] = "1 2 3";
sscanf(buf, "%d %d %d", &a, &b, &c);

 

若拿到的是 "EdisonX Pixnet 200",怎拆解出二個字串一個數字

char buf[200] = "EdisonX Pixnet 200";
char name[50], net[50];
sscanf(buf, "%s %s %d", name, net, &score);

 

甚至,拿到的是 "EdisonX,Pixnet,200" 這種 csv 格式,怎拆解出二個字串一個數字?做法不只一種,但可用 scanf (sscanf) 那種 "不正規" 的 正規表達式 

sscanf(buf, "%[^,],%[^,],%d", name, net, &score);
printf("name:[%s] net:[%s], score:[%d]\n", name, net, score);

 

於是能表達多少,又回歸到了對 sscanf 引數之熟悉度。

----

上面 sprintf 、 sscanf ,即使在 c++ 引入 cstdio 一樣可辦得到,但一些較有個性的 coder ( 也可能是龜毛 ),很不願意在 C++ 中調用這二個函式 (即使有時用這二個真的會較方便 ),較喜愛的是用 stringstream class。

sprintf、sscanf 會有一定風險存在,特別是 sprintf 要注意一些 over flow 問題 ( 雖我一直認為應不會有 coder 犯這毛病),如下一些例子。

 

char buf[10];
double x = 123456;
sprintf(buf, "%lf", x);

 

上面例子裡面,實際上 buf 是會存 123456.000000,已超過 buf size。我認為不容易犯,是因就我所知,有經驗 coder ,都會把 buf size 設很大 (小小的也有 100、200),甚至會直接調用一些 macro 來直接設定 buf 最大值。

離題了。

一般接觸 C++後,仍有機會接觸到 sprintf、sscanf,在見識這二隻的威力後,會願意花點時間學習。但學 C 再學 C++,部份 coder 可能認為,sprintf、sscanf 已夠強大,stringstream 沒什麼必要學。這些看個人喜好。

下面講的,是 stringstream 一些用法,在使用此類別時,必須先引入 sstream。要完全搞清楚的話,弄清 istream、ostream、iostream、stringstreambase、stringstream、istringstream、ostringstream 關係,會有不少幫助。

-----

若要將字串 string ss="123" 轉成整數 int x; 

string ss="123";
 int x;

 stringstream stream;
 stream << ss;
 stream >> x;
 cout << x << endl;

 

也可用在 char ss[] = "123" 上面。現反過來,若將整數 int x 轉成字串 string ss

 

string ss;
 int x=123;

 stringstream stream;
 stream << x;
 stream >> ss ;
 cout << ss << endl;

 

一樣,char ss[200] 也可適用。stringstream 也可產生 1.txt~100.txt 之字串

 

string filename;
stringstream stream;
int i;
for(i=1; i<=100; ++i) {
    stream.clear(); /* 先清除 */
    stream << i << ".txt" ;
    stream >> filename; /* 轉到 filename */
    cout << filename << endl;
}

 

注意到,在loop 裡面有清除 flag 動作,stream.clear()。若要清文字內容,是用 stream.str(""),二者不同。用 stringstream 產生 A001.txt~A100.txt 字串較麻煩一點,要先引入 iomanip

string filename;
stringstream stream;
int i;

for(i=1; i<=100; ++i) {
    stream.clear(); /* 先清除 */
    stream << 'A' << setw(3) << setfill('0') << i << ".txt" ;
    stream >> filename; /* 轉到 filename */
    cout << filename << endl;
}

 

說穿了,最後是在考驗對 cin / cout 與 iomanip 之熟練度。用 stringstream ,對字串 "EdisonX Pixnet 200" 分解出 2 個字串一個整數

string ss = "EdisonX Pixnet 200";
string name, net;
int score;

stringstream stream;
stream << ss;
stream >> name >> net >> score;
cout << "  name:" << name << endl;
cout << "  net :" << net  << endl;
cout << " score:" << score<< endl;

 

再來,用 stringstream 對 csv 字串 "EdisonX,Pixnet,200" 分解的話,較麻煩的做法

 

string ss = "EdisonX,Pixnet,200";
string name, net;
int score;

stringstream stream(ss);
getline(stream, name,',');
getline(stream, net, ',');
stream >> score;

cout << name << endl;
cout << net << endl;
cout << score << endl;

 

有沒有一行可以寫完的?目前我還做不到 (所以比較常用 scnaf / sscanf 系列 ),做到的話再放上來吧。

edisonx 發表在 痞客邦 PIXNET 留言(0) 人氣()