- 今天看了下,大一时写的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
- 结尾处 的 子串
- 这样做,感觉上就比较清晰了。