代码统计工具1.1版本技术文档

代码统计工具1.1版本技术文档

说明:主要记录自己在做这个项目的过程中用到的方法和相关技术

1.首先面临的问题就是怎样选择一个目录,网上搜索了一下,下面是解决方案(用到目录对话框)

(1)从默认的磁盘总目录下开始选择:

TCHARszPath[MAX_PATH];

BROWSEINFObr;

ITEMIDLIST*pItem;

br.hwndOwner=this->GetSafeHwnd();

br.pidlRoot=0;

br.pszDisplayName=0;

br.lpszTitle="选择路径";

br.ulFlags=BIF_STATUSTEXT;

br.lpfn=0;

br.iImage=0;

br.lParam=0;

pItem=SHBrowseForFolder(&br);

if(pItem!=NULL)

{

if(SHGetPathFromIDList(pItem,szPath)==TRUE)

{

//这就是我们得到的目录名称

CStringstrDir=szPath;

}

}

(2)自己设定需要目录对话框默认选择的目录

第一步:(和第一种不同的是需要为这个目录对话框设定自定义回调函数)

TCHARszDefaultDir[MAX_PATH];

CStringstrDef(_T("d://C++//"));//需要设定的默认的目录

memcpy(szDefaultDir,strDef.GetBuffer(strDef.GetLength()),strDef.GetLength());

strDef.ReleaseBuffer();

TCHARszPath[MAX_PATH];

BROWSEINFObr;

ITEMIDLIST*pItem;

br.hwndOwner=this->GetSafeHwnd();

br.pidlRoot=0;

br.pszDisplayName=0;

br.lpszTitle="选择路径";

br.ulFlags=BIF_STATUSTEXT;

//设置CALLBACK函数

br.lpfn=FA_BrowseCallbackProc;

br.iImage=0;

//设置默认路径

br.lParam=long(&szDefaultDir);

/*说明:在Unicode环境下,编译测试,此处的默认路径无法起作用

/*需要手动转换成TChar/WChar

/*TCharstrBuffer[MAX_PATH];

/*wcscpy(strBuffer,szDefaultDir);*/

pItem=SHBrowseForFolder(&br);

if(pItem!=NULL)

{

if(SHGetPathFromIDList(pItem,szPath)==TRUE)

{

//这就是我们得到的目录名称

m_strDirPath=szPath;

}

}

第二步:设计回调函数

intCALLBACKFA_BrowseCallbackProc(HWNDhwnd,UINTuMsg,LPARAMlParam,LPARAMlpData)

{

switch(uMsg)

{

caseBFFM_INITIALIZED://初始化消息

//传递默认打开路径(方法一)

//::SendMessage(hwnd,BFFM_SETSELECTION,TRUE,(LPARAM)"C://ProgramFiles");

//传递默认打开路径(方法二,前提是lpData提前设置好)

::SendMessage(hwnd,BFFM_SETSELECTION,TRUE,lpData);

break;

caseBFFM_SELCHANGED://选择路径变化,

{

charcurr[MAX_PATH];

SHGetPathFromIDList((LPCITEMIDLIST)lParam,curr);

::SendMessage(hwnd,BFFM_SETSTATUSTEXT,0,(LPARAM)curr);

}

break;

default:

break;

}

return0;

}

(3)用到的数据结构(MSDN查看相应介绍):

typedefstruct_browseinfo{

HWNDhwndOwner;

LPCITEMIDLISTpidlRoot;

LPTSTRpszDisplayName;

LPCTSTRlpszTitle;

UINTulFlags;

BFFCALLBACKlpfn;

LPARAMlParam;

intiImage;

}BROWSEINFO,*PBROWSEINFO,*LPBROWSEINFO;

typedefstruct_ITEMIDLIST{

SHITEMIDmkid;

}ITEMIDLIST,*LPITEMIDLIST;

typedefconstITEMIDLIST*LPCITEMIDLIST;

2.遍历一个目录(需要递归遍历下面所有的文件)并保存源代码文件的文件名(后缀名为.c,cpp,.h,.java)

/********************************************************************

*函数名:

FA_ReadDirectory(CStringstrDirPath)

*函数功能:

读取一个目录下的所有文件

*输入参数:

strDirPath:目录的完整路径

*输出参数:

*返回值:

*用到的全局变量和结构:

*其他说明:

********************************************************************/

voidCFA_CodeAnalysisView::FA_ReadDirectory(CStringstrDirPath)

{

WIN32_FIND_DATAtFind={0};

CStringstrTemp;

CStringstrDirTemp;

CStringstrSuffix;

strDirPath.Format("%s//*",strDirPath);

HANDLEhSearch=::FindFirstFile(strDirPath,&tFind);

if(hSearch==INVALID_HANDLE_VALUE)

{

return;

}

//过滤掉...文件目录

::FindNextFile(hSearch,&tFind);

while(::FindNextFile(hSearch,&tFind))

{

strDirTemp=strDirPath;

strTemp.Format("%s",tFind.cFileName);

//去掉最后那一个*通配符

strDirTemp=strDirTemp.Left(strDirTemp.GetLength()-1);

strDirTemp+=strTemp;

if((tFind.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)!=0)

{

FA_ReadDirectory(strDirTemp);

}

strSuffix=strTemp.Right(strTemp.GetLength()-strTemp.Find('.'));

if(!strSuffix.CompareNoCase(".h")||!strSuffix.CompareNoCase(".cpp")||

!strSuffix.CompareNoCase(".c")||!strSuffix.CompareNoCase(".java"))

{

m_strFileName[m_iFileCount]=strDirTemp;

m_iFileCount++;

}

}

::FindClose(hSearch);

}

知识点:FindFirstFileFindNextFile函数以及下面这个结构体.

typedefstruct_WIN32_FIND_DATA{

DWORD dwFileAttributes;

FILETIME ftCreationTime;

FILETIME ftLastAccessTime;

FILETIME ftLastWriteTime;

DWORD nFileSizeHigh;

DWORD nFileSizeLow;

DWORD dwOID;

TCHAR cFileName[MAX_PATH];

}WIN32_FIND_DATA;

3.计算

/********************************************************************

*函数名:

FA_CalculateLines()

*函数功能:

计算各个文件的代码行数,注释函数以及空白函数

*输入参数:

*输出参数:

*返回值:

*用到的全局变量和结构:

*其他说明:

********************************************************************/

voidCFA_CodeAnalysisView::FA_CalculateLines()

{

m_lBlankTotalLines=0;

m_lNoteTotalLines=0;

m_lCodeTotalLines=0;

CStringstrFileName;

CStringstrRecvData;

CStdioFilefile;

BOOLbNoteEnd=FALSE;

intblankLines=0;

intnoteLines=0;

intcodeLines=0;

intblankTotalLines=0;

intnoteTotalLines=0;

intcodeTotalLines=0;

CRectrt;

GetClientRect(rt);

intiAverHigh=rt.Height()/32;

/************************************************************************/

/*以下代码段计算每个文件的有效代码行数,注释行数以及空行数

/*计算所有文件的总的相对应的行数

/*用的是一个从CFile继承的类CStdioFile,因为它有一个方法可以直接读一行文件

/*内容到一个CString

/************************************************************************/

for(inti=0;i<m_iFileCount;i++)

{

strFileName=m_strFileName[i];

//以只读模式打开文件

file.Open(strFileName,CFile::modeRead);

//读入一行带字符串中

while(file.ReadString(strRecvData))

{

//判断是否是多行注释的开头

if(!strRecvData.Left(2).Compare("/*")&&!bNoteEnd)

{

//判断多行注释是否在当前行的结束

if(strRecvData.Right(2).Compare("*/"))

{

bNoteEnd=TRUE;

}

noteLines++;

}

//判断是不是多行注释的结束

elseif(!strRecvData.Right(2).Compare("*/")&&bNoteEnd)

{

noteLines++;

bNoteEnd=FALSE;

}

//判断当前行在多行注释中间部分

elseif(bNoteEnd)

{

noteLines++;

}

//判断是否是空行

elseif(strRecvData.TrimLeft("/t"),strRecvData.IsEmpty())

{

blankLines++;

}

//判断是否是单行注释

elseif(!strRecvData.Left(2).Compare("//"))

{

noteLines++;

}

//否则是有效代码行

else

{

codeLines++;

}

}

//注意用完一个文件后关闭

file.Close();

m_iBlankLines[i]=blankLines;

m_iNoteLines[i]=noteLines;

m_iCodeLines[i]=codeLines;

m_lBlankTotalLines+=blankLines;

m_lNoteTotalLines+=noteLines;

m_lCodeTotalLines+=codeLines;

blankLines=0;

noteLines=0;

codeLines=0;

}

//根据计算结果计算视图总共的高度

if(m_iFileCount>7)

{

CSizesizeTotal;

sizeTotal.cx=600;

sizeTotal.cy=m_iFileCount*iAverHigh*4;

SetScrollSizes(MM_TEXT,sizeTotal);

}

}

知识点:CStdioFile类的使用以及它的函数ReadStirng读入文件的一行到一个字符串

4.输出计算结果:

/********************************************************************

*函数名:

DrawFileText(CDC*pDC)

*函数功能:

输出文件名,及各个计算结果

*输入参数:

pDC:用于输出文字的CDC指针

*输出参数:

*返回值:

*用到的全局变量和结构:

*其他说明:

********************************************************************/

voidCFA_CodeAnalysisView::FA_DrawFileText(CDC*pDC)

{

CStringstrFileName;

pDC->SetBkMode(TRANSPARENT);

CRectrt;

GetClientRect(rt);

intiAverHigh=rt.Height()/32;

pDC->SetTextColor(RGB(0,0,0));

strFileName.Format("此目录下各个行数的总数如下(总共有%d个文件)",m_iFileCount);

pDC->DrawText(strFileName,CRect(0,0,rt.Width(),20),DT_LEFT);

pDC->SetTextColor(RGB(255,0,0));

strFileName.Format("总代码有%d",m_lCodeTotalLines);

pDC->DrawText(strFileName,CRect(50,1*iAverHigh,rt.Width(),1*iAverHigh+20),DT_LEFT);

pDC->SetTextColor(RGB(0,255,0));

strFileName.Format("总注释有%d",m_lNoteTotalLines);

pDC->DrawText(strFileName,CRect(50,2*iAverHigh,rt.Width(),2*iAverHigh+20),DT_LEFT);

pDC->SetTextColor(RGB(0,0,255));

strFileName.Format("总空行有%d",m_lBlankTotalLines);

pDC->DrawText(strFileName,CRect(50,3*iAverHigh,rt.Width(),3*iAverHigh+20),DT_LEFT);

for(inti=0;i<m_iFileCount;i++)

{

strFileName=m_strFileName[i];

pDC->SetTextColor(RGB(0,0,0));

pDC->DrawText(strFileName.Right(strFileName.GetLength()-m_strDirPath.GetLength()-1),

CRect(0,(i+1)*4*iAverHigh,rt.Width(),(i+1)*4*iAverHigh+20),DT_LEFT);

pDC->SetTextColor(RGB(255,0,0));

strFileName.Format("代码有%d",m_iCodeLines[i]);

pDC->DrawText(strFileName,CRect(50,((i+1)*4+1)*iAverHigh,rt.Width(),

((i+1)*4+1)*iAverHigh+20),DT_LEFT);

pDC->SetTextColor(RGB(0,255,0));

strFileName.Format("注释有%d",m_iNoteLines[i]);

pDC->DrawText(strFileName,CRect(50,((i+1)*4+2)*iAverHigh,rt.Width(),

((i+1)*4+2)*iAverHigh+20),DT_LEFT);

pDC->SetTextColor(RGB(0,0,255));

strFileName.Format("空行有%d",m_iBlankLines[i]);

pDC->DrawText(strFileName,CRect(50,((i+1)*4+3)*iAverHigh,rt.Width(),

((i+1)*4+3)*iAverHigh+20),DT_LEFT);

}

}

5.运行效果

6.总结

此项目虽然很小,但是比较实用,我们可以简单的计算一个目录下或是一个工程有多少代码行,注释行以及空白行。对于自己编程多少的检验,以及一个团队内每个成员的编程多少做统计。

<!--EndFragment-->

你可能感兴趣的:(代码)