- 今天看了下,大一時寫的C程序,雖然命名真的不太規範,但整體感覺還好,程序邏輯很清
- 晰、也比較高效。這的確是我編程的風格,呵呵。我不太喜歡很臃腫的代碼,喜歡將它們寫得足夠
- 簡潔,而邏輯上也足夠清晰。
- 舉個小小的例子,一個大家熟知的 split 函數,它完成的功能就是:
- 給定一個字符串,然後通過指定的分隔符,將字符串分成N個子串,就這麼簡單。
- 現在給出程序1
- void Split( const string& s, char c, vector<string>& v )
- {
- int i = 0;
- int j = s.find(c,0);
- while (j >= 0)
- {
- v.push_back(s.substr(i, j-i));
- i = ++j;
- j = s.find(c, j);
- if (j < 0)
- {
- v.push_back(s.substr(i, s.length( )));
- }
- }
- }
- 第一眼看它的時候,你或許會納悶 爲什麼會有這個 if (j < 0),你試下 去掉這條語句,
- 就會發現一個bug,就是最後條子串不會被添加到 v 中,例如: s = "ht;t2;t3;t4", c = ';'時,
- 調用Split時,t4就不會加到v中。
- 原因很簡單:在 i 指向 't'的時候,這條語句 j = s.find( c, j ); 會因爲找不到 ';' 而返回
- -1。
- 但是這個函數的寫作者知道了這種情況,在後面添加了 if ( j < 0 ) ...解決了這個bug。。。
- 是這樣的嗎?可別被我誘導了,實際上真正的bug在於當字符串裏面沒有 ';'時,問題就來了,例如
- : s = "http", c = ';',int j = s.find(c,0); 執行後 j = -1; while根本不會運行,也就是說
- ,v裏面根本就沒有數據了。顯然這和該函數完成的功能是不吻合的。
- 實際上,通過分析上面的程序,我們發現我們只是漏掉了最後一個子串的情況,因此,別的代碼不
- 用做改變,只用在最後將 s 裏的最後一個子串 插入到 v 中就OK了。
- 我們可以先去掉這個 while 裏邊的 if 。 然後在while()外面,將最後的那個子串插入到 v 中,
- 這顯然可行。而且,比上面的效率要高。修改之後的程序2爲:
- 程序2:
- void Split( const string& s, char c, vector<string>& v )
- {
- int i = 0;
- int j = s.find(c,0);
- while (j >= 0)
- {
- v.push_back(s.substr(i, j-i));
- i = ++j;
- j = s.find(c, j);
- }
- v.push_back( s.substr(i, -1) );
- }
- 最後一句代表把 s.substr(i,-1)表示從下標i 開始到 s結尾處 的一個子串。顯然在字符串分隔符
- 很多的情況下,它比程序一節省了將近一半的時間。
- 在這裏,你可能會說我太拘泥於細節了。的確,有時候,編程的確需要有很好的細節把握能力,但
- 這並不是過分的表現。可以這麼說,不會把握細節的程序員,不是一個好的程序員。
- 回到正題,我甚至覺得上面那段還是不大好看,能不能再簡潔些呢。我們仔細分析下,可發現,只
- 要 s 不爲空的情況下,
- v裏面的子串就至少有一個,也就是說,循環至少應該被執行一次。那個循環與此相同,對!用 do
- .. while循環。我將修改後的代碼直接給出:
- 程序3:
- void Split( const string& s, char c, vector<string>& v )
- {
- if ( s.empty() )
- return;
- int i;
- int j = -1;
- do
- {
- i = ++j;
- j = s.find( c, j );
- v.push_back( s.substr(i, j-i) );
- } while ( j > 0 );
- }
- 你可能會擔心在 j < 0(也就是j==-1) 之後,v.push_back(s.substr(i, j-i)); 是否正確。
- 是的,當 j = -1 時,j - i 顯然是一個負數,在這樣的情況下,s.substr將包含從下標從 i 到 s
- 結尾處 的 子串
- 這樣做,感覺上就比較清晰了。