代碼統計工具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("http://"))
{
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.總結
此項目雖然很小,但是比較實用,我們可以簡單的計算一個目錄下或是一個工程有多少代碼行,注釋行以及空白行。對于自己編程多少的檢驗,以及一個團隊內每個成員的編程多少做統計。
浙公網安備 33010602011771號