C++ 一个统计文件夹下所有代码文件行数的小工具

时间:2023-01-18 17:31:14
// CodeLines.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <Commdlg.h>
#include <Shlobj.h>
#include <map>
#include <vector>
#include <iostream>
#include <io.h>
#include <string>
#include <set>
#include <fstream>
#pragma comment(lib,"Shell32.lib")

void InitValidTypes(std::set<std::string> &validTypes)
{
    validTypes.insert(".h");
    validTypes.insert(".cpp");
    validTypes.insert(".c");
    validTypes.insert(".hpp");
    validTypes.insert(".py");
    validTypes.insert(".txt");
    validTypes.insert(".cs");
    validTypes.insert(".css");
    validTypes.insert(".js");
    validTypes.insert(".lua");
    validTypes.insert(".html");
    validTypes.insert(".htm");
    validTypes.insert(".java");
    validTypes.insert(".bat");
    validTypes.insert("makefile");
}

int32_t WToChar(LPWSTR szWstr, char szCstr[], const int32_t chrLen)
{
    int iLength = WideCharToMultiByte(CP_ACP, 0, szWstr, -1, NULL, 0, NULL, NULL);
    return WideCharToMultiByte(CP_ACP, 0, szWstr, -1, szCstr, iLength, NULL, NULL);
}

void OpenDirectoryDialog(HWND hWnd, char directoryPath[], const int32_t directoryPathLen)
{
    TCHAR szBuffer[MAX_PATH] = { 0 };
    BROWSEINFO bi = { 0 };
    bi.hwndOwner = hWnd;//拥有着窗口句柄,为NULL表示对话框是非模态的,实际应用中一般都要有这个句柄  
    bi.pszDisplayName = szBuffer;//接收文件夹的缓冲区  
    bi.lpszTitle = TEXT("选择一个文件夹");//标题  
    bi.ulFlags = BIF_NEWDIALOGSTYLE;
    BOOL ret = FALSE;
    do{
        LPITEMIDLIST idl = SHBrowseForFolder(&bi);
        ret = SHGetPathFromIDList(idl, szBuffer);
        if (!ret)
        {
            MessageBox(NULL, TEXT("请选择一个文件夹"), NULL, MB_ICONERROR);
        }
        else
        {
            //MessageBox(NULL, szBuffer, TEXT("你选择的文件夹"), 0);
            WToChar(szBuffer, directoryPath, directoryPathLen);
        }
    } while (!ret);
}

void GetFilesFromDirectory(std::vector<std::string> &files, const char *directoryPath)
{
    struct _finddata_t fileinfo;
    long hFile = 0;
    char tmpPath[MAX_PATH] = { 0 };
    sprintf_s(tmpPath, "%s\\*", directoryPath);
    if ((hFile = _findfirst(tmpPath, &fileinfo)) == -1){ return; }
    do
    { 
        if ((fileinfo.attrib &  _A_SUBDIR))
        {
            if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
            {
                sprintf_s(tmpPath, "%s\\%s", directoryPath, fileinfo.name);
                GetFilesFromDirectory(files, tmpPath);
            }
        }
        else
        {
            sprintf_s(tmpPath, "%s\\%s", directoryPath, fileinfo.name);
            files.push_back(tmpPath);
        }
    } while (_findnext(hFile, &fileinfo) == 0);
    _findclose(hFile);
}

void StringToLower(std::string &str)
{
    for (uint32_t i = 0; i < str.length(); ++i)
    {
        if (str[i] >= 'A' && str[i] <= 'Z')
        {
            str[i] = str[i] + 'a' - 'A';
        }
    }
}

std::string GetFileExtention(const std::string &fileName)
{
    std::string localFileName = fileName;
    StringToLower(localFileName);
    int pos = localFileName.find_last_of('.');
    if (pos >= 0)
    {
        return localFileName.substr(pos);
    }
    return localFileName;
}

void DumpFiles(const std::vector<std::string> &files)
{
    for (uint32_t i = 0; i < files.size() && i < 100; ++i)
    {
        std::cout << files[i] << std::endl;
        std::cout << GetFileExtention(files[i]) << std::endl;
    }
    std::cout << files.size() << std::endl;
}

bool IfFileTypeValid(const std::string &fileExtention)
{
    static std::set<std::string> validTypes;
    if (validTypes.empty()){
        InitValidTypes(validTypes);
    }
    return validTypes.find(fileExtention) != validTypes.end();
}

uint32_t CalculateCharShowTimesInBuf(char buf[], const uint32_t len, char aim)
{
    uint32_t i = 0,counter = 0;
    while (i < len && buf[i] != '\0')
    {
        if (buf[i] == aim){ ++counter; }
        ++i;
    }
    return counter;
}

uint32_t GetFileLines(const std::string &fileName)
{
    const uint32_t maxBufLen = 10240;
    static char buf[maxBufLen + 1];
    std::ifstream inFile(fileName, std::ios::in);
    if (!inFile){ return 0; }
    uint32_t counter = 0;
    while (!inFile.eof())
    {
        memset(buf, 0, maxBufLen);
        inFile.read(buf, maxBufLen);
        //std::cout << buf;
        counter += CalculateCharShowTimesInBuf(buf, maxBufLen, '\n');
    }
    return counter;
}

void StatisticCodeLines(std::map<std::string, uint32_t> &codeLines, const std::string &directoryPath)
{
    static std::vector<std::string> files;
    files.clear();
    GetFilesFromDirectory(files, directoryPath.c_str());
    // DumpFiles(files);
    for (const std::string &fileName : files)
    {
        std::string fileExtention = GetFileExtention(fileName);
        if (!IfFileTypeValid(fileExtention)){ continue; }
        codeLines[fileExtention] += GetFileLines(fileName);
    }
}

void PrintCodeLines(const std::map<std::string, uint32_t> &codeLines)
{
    for (const std::pair<std::string, uint32_t> &p : codeLines)
    {
        printf("%-6s:%-8u\n", p.first.c_str(), p.second);
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    char directoryPath[MAX_PATH] = { 0 };
    OpenDirectoryDialog(NULL, directoryPath, MAX_PATH);
    std::cout << directoryPath << std::endl;
    static std::map<std::string, uint32_t> codeLines;
    codeLines.clear();
    StatisticCodeLines(codeLines, directoryPath);
    PrintCodeLines(codeLines);
    getchar();
    return 0;
}

效果图: 

C++ 一个统计文件夹下所有代码文件行数的小工具