您的位置:首页 > 其它

从字符串中查找字符出现次数的方法和性能对比

2008-04-08 17:54 549 查看
今天在一场“特殊的讨论”中引入了一个问题,如何在C#求出字符串中某字符的出现次数,比如求“ADSFGEHERGASDF”中“A”出现的次数。首先想到的方法当然是从头遍历字符串并统计:

c1 = 0;
for (int i = 0; i < str.Length; i++)
{
if (str[i] == 'A')
{
c1++;
}
}

第二种方法也很容易想到,将字符串中所有要查找的字符去除,然后比较去除前后的字符串长度即可。这种方法遭到了某人的鄙视,据说性能很差而且多占空间。

c2 = str.Length - str.Replace("A", String.Empty).Length;

接下来某人又提出了第三种方法,是用要查找的字符为分隔符,将原字符串分隔为多个子串,然后求子串的数目即可。在C#中这是一个写起来很短的方法:

c3 = str.Split(new char[] { 'A' }).Length - 1;

我们从原理可以推断出三者性能的顺序,但究竟差距是多少呢,还是要动手试验一下。这是非常经典的测试代码:

string str = "SADTHDGSAFSDGTGHRDGSADFADDRHDFSGASDAA";

Stopwatch sw = new Stopwatch();

long t;
int c = 0;
GC.Collect();
Application.DoEvents();

sw.Start();

for (int i = 0; i < 100000; i++)
{
c = 三种算法
}

sw.Stop();

t = sw.ElapsedMilliseconds;

首先我们确保正确性,经测试三种方法都能正确处理多种情况,包括首尾、连续出现、不出现或串长度为0等,我所取的字符串是一个很普通的串。编译为Release版,预运行10次后获得以下结果:

遍历统计:13毫秒
替换后比较长度:112毫秒
断开字符串后计数:233毫秒

这里已经体现出差异,遍历统计比替换后比较要快10倍,断开字符串又要慢一些。接下来我又做了如下两个测试:

1、不改变字符串的长度,增加或减少要查找字符串的个数。
2、不改变要查找字符出现的频率,但增长字符串的长度。

结果发现,三种方法都随字符串长度增加线性变慢,而后两种方法还随要查找的字符增加而变慢。断开字符串的方法还受要查找字符串分布情况的影响。

研究Replace函数和Split函数的实现可以彻底解决这个问题。不过我没有心情细细研究了,我还是决定选用第二种方法——替换后比较长度。虽然其速度比第一种方法慢,但易于改写为求长度不为1的子串出现次数的方法。第一种方法若改为求长度大于1的字串就要考虑很多因素(尽管不一定真的很麻烦),我懒得想了,呵呵。

打印 | posted on 2004年11月23日 18:08 | Filed Under [ 技术随笔 ] | 收藏本页 (百度搜藏)(QQ书签)(Live收藏)(Google书签)(Yahoo书签)(新浪ViVi)(搜狐网摘)(365Key网摘)(天极网摘)(博采网摘)(和讯网摘)

反馈




# re: 从字符串中查找字符出现次数的方法和性能对比
如果用第一种方法,子串越长,效率越高。连测试代码都写了,也提了某人的名字n次,还在乎这点小事:
if (str[i] == 'A')
{
c1++;
}
比较改为strncmp,然后i=i+子串长度即可,

:-)
2004/11/23 19:18 | bruise




# re: 从字符串中查找字符出现次数的方法和性能对比
难道不能用正则表达式?
2004/11/23 20:10 | bestcomy




# re: 从字符串中查找字符出现次数的方法和性能对比
其实方法多得要死,我只是闲暇无事测来看看。不要太当真萨。

很多方法要做字符串长度判断或者边界条件判断,如此简单又不是特别需要性能的地方就从简考虑哈
2004/11/23 20:26 | Ninputer




# re: 从字符串中查找字符出现次数的方法和性能对比
我通常使用第二种。
毕竟效率不是唯一考虑的因素。
2004/11/24 8:40 | jiezhi




# re: 从字符串中查找字符出现次数的方法和性能对比
能否讲解一下Stopwatch?
2004/11/24 9:54 | kkk




# re: 从字符串中查找字符出现次数的方法和性能对比
拜托,1,3不是我昨天写的code?
不尊重我的知识产权,你更要请客了
2004/11/24 11:19 | 小峰




# re: 从字符串中查找字符出现次数的方法和性能对比
我就知道一堆MVP跑不出什么别的题来……
2004/11/24 12:25 | jiangsheng




# re: 从字符串中查找字符出现次数的方法和性能对比
我怎么觉得使用除过1之外的方法时,已经包含了1的方法;
这样所有其它的方法必然比1的方法效率低.
应为不论采用什么方法都要识别出待查找的字符串,这样已经可以计数了呀,为什么还要做别的事?
当然边界条件是每个程序都应该考虑的.
2004/11/25 17:30 | iami




# re: 从字符串中查找字符出现次数的方法和性能对比
To bruise :
为什么我用以下代码反而最费时,而且字符串越长,性能差异越明显,远不及Replace快,甚至比不上Spilit
int c = 0;
for (int i = 0; i < sourceString.Length-childString.Length+1; i++)
{
if (string.Compare(sourceString,i,childString,0,childString.Length) == 0)
{
c++;
i += childString.Length;
}
}
不过自己写的
int c = 0;
for (int i = 0; i < sourceString.Length-childString.Length+1; i++)
{
int iMatch = 0;
for (int j = 0; j < childString.Length; j++)
{

if (sourceString[i + iMatch] != childString[j])
{
break;

}
else
{
iMatch++;
}
}
if (iMatch == childString.Length)
{
c++;
i += childString.Length;
}
}
倒是比string.Compare快,不过还是不及Replace
迷惑。。。。。。。
2004/11/28 22:01 | Flyfox




# re: 从字符串中查找字符出现次数的方法和性能对比
最快当然是直接对内存操作,通过对象来操作本来就慢了,对象的replace可能内部实现会比较优化,而自己写算法,却以对象为操作单位,开销会大也很正常……
2004/12/23 16:28 | superliufa




# re: 从字符串中查找字符出现次数的方法和性能对比
在循环中使用对象时,应该用固定的变量来代替对象,比如:
for (int i = 0; i < sourceString.Length-childString.Length+1; i++)
可以换成
int Slen = sourceString.Length;
int Clen = childString.Length;
for (int i = 0; i < Slen -Clen +1; i++)
{
}
2005/1/4 10:57 | CSQ_CPU




# re: 从字符串中查找字符出现次数的方法和性能对比
仁兄做的例程没有代表性,因为在源书符串查询的子符正好长充为1,如果长度大于1.那么第二种方法能突出其优势.
2005/6/8 11:10 | 老刘




# re: 从字符串中查找字符出现次数的方法和性能对比
能否给一个完整的程序
2005/9/10 9:33 | meilidexinshijie




# re: 从字符串中查找字符出现次数的方法和性能对比
都什么年代了,用的又不是8086,快点实现最重要,要不然市场早就被他人占了.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐