前面的文章里已经介绍了如何分割字符串
- 实现一个简单的代码字计数器(四)
互联网上有很多字计数器,但是大部分都是统计一个文本里的字数,在代码中的字计数和一段文本里的计数不太一样;比如下面的代码中计数"name"的个数,代码中单词(字)的个数是有用的;所以这篇文章里先谈一下代码字计数器的好处:
用处如下:首先是可以定位比较重要的对象,因为一般出现频率较高的单词可能在某个函数中比较重要。就拿我上面的代码举例子,如果逐行看下来可能并不知道我这个类想干什么,但是根据单词出现频率可以推断出是和name和address的一个类;有人会说这样并不明显,好,我们举下一个例子:
bool CSetting::ReadValue( CRegKey ®Key, const wchar_t *valName )
{
// bool, int, hotkey, color
if (type==CSetting::TYPE_BOOL || (type==CSetting::TYPE_INT && this[1].type!=CSetting::TYPE_RADIO) || type==CSetting::TYPE_HOTKEY || type==CSetting::TYPE_HOTKEY_ANY || type==CSetting::TYPE_COLOR)
{
DWORD val;
if (regKey.QueryDWORDValue(valName,val)==ERROR_SUCCESS)
{
if (type==CSetting::TYPE_BOOL)
value=CComVariant(val?1:0);
else
value=CComVariant((int)val);
return true;
}
return false;
}
// radio
if (type==CSetting::TYPE_INT && this[1].type==CSetting::TYPE_RADIO)
{
ULONG len;
DWORD val;
if (regKey.QueryStringValue(valName,NULL,&len)==ERROR_SUCCESS)
{
CString text;
regKey.QueryStringValue(valName,text.GetBuffer(len),&len);
text.ReleaseBuffer(len);
val=0;
for (const CSetting *pRadio=this+1;pRadio->type==CSetting::TYPE_RADIO;pRadio++,val++)
{
if (_wcsicmp(text,pRadio->name)==0)
{
value=CComVariant((int)val);
return true;
}
}
}
else if (regKey.QueryDWORDValue(valName,val)==ERROR_SUCCESS)
{
value=CComVariant((int)val);
return true;
}
return false;
}
// string
if (type>=CSetting::TYPE_STRING && type<CSetting::TYPE_MULTISTRING)
{
ULONG len;
if (regKey.QueryStringValue(valName,NULL,&len)==ERROR_SUCCESS)
{
value.vt=VT_BSTR;
value.bstrVal=SysAllocStringLen(NULL,len-1);
regKey.QueryStringValue(valName,value.bstrVal,&len);
return true;
}
return false;
}
// multistring
if (type==CSetting::TYPE_MULTISTRING)
{
ULONG len;
if (regKey.QueryMultiStringValue(valName,NULL,&len)==ERROR_SUCCESS)
{
value.vt=VT_BSTR;
value.bstrVal=SysAllocStringLen(NULL,len-1);
regKey.QueryMultiStringValue(valName,value.bstrVal,&len);
for (int i=0;i<(int)len-1;i++)
if (value.bstrVal[i]==0)
value.bstrVal[i]='\n';
return true;
}
else if (regKey.QueryStringValue(valName,NULL,&len)==ERROR_SUCCESS)
{
value.vt=VT_BSTR;
value.bstrVal=SysAllocStringLen(NULL,len);
regKey.QueryStringValue(valName,value.bstrVal,&len);
if (len>0)
{
value.bstrVal[len-1]='\n';
value.bstrVal[len]=0;
}
return true;
}
return false;
}
Assert(0);
return false;
}
这段代码是我从其他博客直接拷贝过来的一部分,是一个ReadValue函数,如果逐行读下来很有可能并不知道这段代码想表达什么,但是根据单词出现频率发现出现比较多的是"value"这个单词,所以我们可以知道这个函数的核心一定是value。然后就可以直接观看和value相关的代码,发现value第一次出现在函数内部并不是通过定义,那么由于不知道其他代码我们可以假定这个value可能是个全局变量也可能是个类;再进一步会发现value多数都是赋值操作,所以就可以推测出这个ReadValue函数的用途可能是将数据赋值到一个类成员value中。通过这种方式能比较快速的了解函数的用途。
另一个用处是可以更好的理解输入的用途,比如在上面的ReadValue函数中,输入为regKey和valName,我们可以发现regKey和valName基本上出现次数相近而且基本是同时在使用;所以我们可以推断这两个变量很有可能是出自同一个对象的,因为2个变量需要同时进行更新。
再有用处就是:经常出现的模式是在代码的一部分中频繁地使用单词,并且在这一部分之外很少使用。这可能意味着这部分代码集中于使用一个特定的对象,它阐明了这部分代码的职责。