C++ field parsing manipulator for istream

看板C_and_CPP (C/C++)作者 (Khoguan Phuann)時間19年前 (2005/05/23 22:12), 編輯推噓0(000)
留言0則, 0人參與, 最新討論串1/1
※ 引述《khoguan (Khoguan Phuann)》之銘言: : ※ 引述《penggw (~夢的點滴~)》之銘言: : : 現在我要輸入一個時間 : : 格式 : : 例: 12:59 : : 9:00 : : 把小時存在int hour裡面 : : 分鐘存在int min裡面 : : scanf("%d:%d",&hour,&min)可以輸入 : : 那用cin要如何達成同樣目的? : C++ standard library 沒有額外設計類似 scanf 這種 : 功能的函式可以用。或者繼續沿用 scanf 或者就要寫得 : 比較麻煩一點。像是 : char buf[3]; : cin.getline(buf, 3, ':'); // 到 : 為止 : int hour = atoi(buf); : cin.getline(buf, 3); // 到 end of line 為止 : int min = atoi(buf); 以上是簡單的做法。因為我感到 field parsing 是常常要用到 的操作,C++ 標準程式庫未能提供現成的東西可以用,一時手癢, 就寫了一個比較一般化的解法,可以指定一種或多種欄位分隔號。 因為已經超出 penggw 的需求,因此我重訂了標題,尚祈海涵。 我寫的工具,使用的方法,是一種 I/O manipulator 技巧的應用: Field timef(":"); // 指定用 : 做為欄位分隔符號,可以一次指定多種符號 // 讀進的資料只要遇到其中任何一個都視為欄位分隔號 // 連續兩個分隔號,中間仍算是一欄,為空資料欄位 // 預設以一行為record,最後一欄後面不要再加分隔符 //Fild timef(":", false); // 這樣則表示最後一欄後面要加分隔符,一欄的 // 資料就可以跨行,但是 client code 要自行 // 處理掉後面的多餘字元(包括'\n'),例如使用 // cin.ignore(INT_MAX, '\n'); // 後續的輸入才不會有問題 // 輸入整數的例子 int hour = 0, min = 0; cin >> timef(hour) >> timef(min); //如12:23的12存到 hour, 23存到 min // 輸入字串的例子 Field namef("|;"); // | 和 ; 都視為分隔號 string name1, name2, name3; cin >> namef(name1) >> namef(name2) >> namef(name3); /* 輸入 Brian W. Kernighan|Dennis M. Ritchie;Bjarne Stroustrup 以後,name1 == Brian W. Kernighan name2 == Dennis M. Ritche name3 == Bjarne Stroustrup */ 用 string 去接,可以輸入任意長度的字串。也可以用 char arrary 去接,程式會自動將超出的輸入部份丟掉,不會 overflow. 如 char n1[30], n2[30], n3[30]; cin >> namef(n1) >> namef(n2) >> namef(n3); char array 和 char* 不同,若是用 char* 去接,只會存到whitespace 之前,並且輸入欄位長度不能超過 buffer 各種 type 也可混合在一起使用。 用起來還不錯 :-) 不過若要更複雜的 parsing 功能,可能就得 借用行家所做的 regular expression library 了。 程式碼實作如下,敬請批評指教。 /* * C++ Field Parsing Manipulator for istream * by Khoguan Phuann, 2005/05/23 */ #include <iostream> #include <string> #include <cstring> #include <sstream> using namespace std; template <class T> struct GetField; class Field { public: Field(const string& d = " ", bool e = true) : di(d), eol(e) { } template <class T> GetField<T> operator() (T& val) { return GetField<T>(*this, val); } // 3 friend operator>> template <class T> friend istream& operator>> (istream& is, const GetField<T>& gf); template <int LEN> friend istream& operator>> (istream& is, const GetField<char[LEN]>& gf); friend istream& operator>> (istream& is, const GetField<string>& gf); private: string di; // delimiters string bool eol; // wheter \n serves as delimiter }; template <class T> struct GetField { GetField(const Field& ff, T& v) : f(ff), val(v) { } const Field& f; T& val; }; template <class T> istream& operator>> (istream& is, const GetField<T>& gf) { string buf; char ch; while (is.get(ch)) { if (gf.f.di.find(ch) != -1 || (gf.f.eol && ch == '\n')) { istringstream istr(buf); istr >> gf.val; break; } buf += ch; } return is; } istream& operator>> (istream& is, const GetField<string>& gf) /* overloaded for GetField<string> */ { string tmp; char ch; while (is.get(ch)) { if (gf.f.di.find(ch) != -1 || (gf.f.eol && ch == '\n')) break; tmp += ch; } gf.val = tmp; return is; } template <int LEN> istream& operator>> (istream& is, const GetField<char[LEN]>& gf) /* overloaded for GetField<char[LEN]> */ { string buf; char* pc = gf.val; char ch; while (is.get(ch)) { if (gf.f.di.find(ch) != -1 || (gf.f.eol && ch == '\n')) break; buf += ch; } strncpy(pc, buf.c_str(), LEN-1); pc[LEN-1] = 0; return is; } /* End of C++ Field Parsing Manipulator for istream */ -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 220.130.208.168
文章代碼(AID): #12aUJgh6 (C_and_CPP)
文章代碼(AID): #12aUJgh6 (C_and_CPP)