zoukankan      html  css  js  c++  java
  • 代码统计工具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.总结

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

     

  • 相关阅读:
    java第一章到第四章
    Linux Foundation Secure Boot System Released
    linux下avr单片机开发:中断服务程序
    【LeetCode】Binary Tree Level Order Traversal II
    java 学习笔记4
    动手写快排
    [置顶] 在Ubuntu下实现一个简单的Web服务器
    mahout算法源码分析之Collaborative Filtering with ALS-WR (四)评价和推荐
    hdu 4758 Walk Through Squares
    MediaInfo源代码分析 2:API函数
  • 原文地址:https://www.cnblogs.com/brucewoo/p/2252036.html
Copyright © 2011-2022 走看看