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

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

 

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

 

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

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

TCHAR szPath[MAX_PATH];

BROWSEINFO br;

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)

{

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

CString strDir = szPath;

}

}

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

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

TCHAR szDefaultDir[MAX_PATH];

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

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

strDef.ReleaseBuffer();

 

TCHAR szPath[MAX_PATH];

BROWSEINFO br;

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

/*       TChar strBuffer[MAX_PATH];

/*       wcscpy(strBuffer, szDefaultDir);*/

 

pItem = SHBrowseForFolder(&br);

if (pItem != NULL)

{

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

{

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

m_strDirPath = szPath;

}

}

第二步:设计回调函数

int CALLBACK FA_BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)

{

switch(uMsg)

{

case BFFM_INITIALIZED:    //初始化消息

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

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

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

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

break;

case BFFM_SELCHANGED:    //选择路径变化,

{

char curr[MAX_PATH];   

SHGetPathFromIDList((LPCITEMIDLIST)lParam,curr);   

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

}

break;

default:

break;

}

 

return 0;   

}

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

typedef struct _browseinfo { 

    HWND hwndOwner; 

    LPCITEMIDLIST pidlRoot; 

    LPTSTR pszDisplayName; 

    LPCTSTR lpszTitle; 

    UINT ulFlags; 

    BFFCALLBACK lpfn; 

    LPARAM lParam; 

    int iImage; 

} BROWSEINFO, *PBROWSEINFO, *LPBROWSEINFO; 

 

typedef struct _ITEMIDLIST { 

SHITEMID mkid;

} ITEMIDLIST, * LPITEMIDLIST; 

typedef const ITEMIDLIST * LPCITEMIDLIST;

 

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

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

函数名:

FA_ReadDirectory(CString strDirPath)

函数功能:

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

输入参数

strDirPath:目录的完整路径

输出参数

返回值

用到的全局变量和结构

其他说明:

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

void CFA_CodeAnalysisView::FA_ReadDirectory(CString strDirPath)

{

WIN32_FIND_DATA tFind = {0};

CString strTemp;

CString strDirTemp;

CString strSuffix;

 

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

HANDLE hSearch = ::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); 

}

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

typedef struct _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()

函数功能:

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

输入参数

输出参数

返回值

用到的全局变量和结构

其他说明:

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

void CFA_CodeAnalysisView::FA_CalculateLines()

{

m_lBlankTotalLines = 0;

m_lNoteTotalLines = 0;

m_lCodeTotalLines = 0;

 

CString strFileName;

CString strRecvData;

CStdioFile file;

BOOL bNoteEnd = FALSE;

int blankLines = 0;

int noteLines = 0;

int codeLines = 0;

int blankTotalLines = 0;

int noteTotalLines = 0;

int codeTotalLines = 0;

 

CRect rt;

GetClientRect(rt);

int iAverHigh = rt.Height() / 32;

 

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

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

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

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

/*内容到一个CString

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

    for (int i=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++;

}

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

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

{

noteLines++;

bNoteEnd = FALSE;

}

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

else if (bNoteEnd)

{

noteLines++;

}

//判断是否是空行

else if (strRecvData.TrimLeft("/t "), strRecvData.IsEmpty())

{

blankLines++;

}

//判断是否是单行注释

else if (!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)

{

CSize sizeTotal;

sizeTotal.cx = 600;

sizeTotal.cy = m_iFileCount * iAverHigh * 4;

SetScrollSizes(MM_TEXT, sizeTotal);

}

}

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

 

4.输出计算结果:

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

函数名:

DrawFileText(CDC *pDC)

函数功能:

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

输入参数

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

输出参数

返回值

用到的全局变量和结构

其他说明:

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

void CFA_CodeAnalysisView::FA_DrawFileText(CDC *pDC)

{

CString strFileName;

 

pDC->SetBkMode(TRANSPARENT);

CRect rt;

GetClientRect(rt);

int iAverHigh = 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 (int i=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.总结

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

 

posted @ 2010-07-08 20:22  蔷薇理想人生  阅读(346)  评论(0编辑  收藏  举报