最近在找工作,某公司出了这么个题:统计项目源代码行数。

为了实现跨平台用了boost,g++和vs2010编译通过。代码如下:

FileTravel.h

#pragma once
#include <string>
#include <vector>
#include <iostream>
using namespace std;


class FileTravel
{
public:
	FileTravel(void);
	FileTravel(const string& dirpath);
	~FileTravel(void);
	void SetFilterTypes(const vector<string> filtertypes);
	bool IsFilterFile(const string &filepath);
	void GetSubfiles(vector<string> & subfilepaths);
private:	
	string _dirpath;				//源文件目录
	vector<string> _subfilepaths;	//源文件目录下的所有源文件路径
	vector<string> _filterTypes;	//后缀名过滤源文件类型
};

FileTravel.cpp

#include "FileTravel.h"
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>

FileTravel::FileTravel(void)
	: _dirpath(NULL)
{
	_filterTypes.clear();
	_subfilepaths.clear();
}

FileTravel::~FileTravel(void)
{
}

FileTravel::FileTravel(const string& dirpath)
	: _dirpath(dirpath)
{
}

/*
	获取源文件目录下的所有代码源文件
*/
void FileTravel::GetSubfiles(vector<string> & subfilepaths)
{
	namespace fs = boost::filesystem;
	fs::path rootpath(_dirpath, fs::native);
	if(!fs::exists(rootpath))
		return;
	fs::recursive_directory_iterator endIter;

	for(fs::recursive_directory_iterator iter(rootpath); iter != endIter; iter++) {
		try{
			if(fs::is_regular_file(*iter)){
				if(IsFilterFile(iter->path().string())) {
					subfilepaths.push_back(iter->path().string());
					cout << *iter << " is a file" << std::endl;
				}
			}
		} catch ( const std::exception & ex ){
			std::cerr << ex.what() << std::endl;
			continue;
		}
	}

}

void FileTravel::SetFilterTypes(const vector<string> filtertypes)
{
	_filterTypes.clear();
	_filterTypes.assign(filtertypes.begin(),filtertypes.end());
}

/*
	过滤,判断是否是_filterTypes后缀的源文件类型
*/
bool FileTravel::IsFilterFile(const string &filepath)
{
	if(_filterTypes.size() == 0)
		return false;
	vector<string>::iterator iter = _filterTypes.begin();
	int len = filepath.length();
	while( iter != _filterTypes.end() && filepath.rfind(*iter) != len-(*iter).length()) 
		iter++;
	if(iter == _filterTypes.end())
		return false;
	return true;
}

SourceLine.h

#pragma once
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <string>
#include <vector>
using namespace boost;
using namespace std;

typedef enum linetype {
	NOTYPE_LINE,				//在空行、注释行、代码行类型之外的类型
	EMPTY_LINE,					//空行
	CODE_LINE,					//代码行
	NOTE_SINGLE_LINE,			//单行注释
	NOTE_MULTI_LINE				//多行注释
}linetype;

typedef struct  noteString{
	string singlenotestr;
	string multnote_startstr;
	string multnote_endstr;
	noteString()
	{
	
	}
	noteString(const noteString& notestr)
	{
		singlenotestr = notestr.singlenotestr;
		multnote_startstr = notestr.multnote_startstr;
		multnote_endstr = notestr.multnote_endstr;
	}
}noteString;

class SourceLine
{
public:
	SourceLine(void);
	SourceLine(const vector<string>& filepaths, int start, int end);
	static unsigned long GetTotallines(void);
	static unsigned long GetEmptylines(void);
	static unsigned long GetNotelines(void);
	static unsigned long GetValidCodelines(void);
	void CalculateLine(void);
	void SetNoteString(const noteString& notestr);
	~SourceLine(void);
private:	
	static void incLine(linetype tp);
	linetype analyisLine(string& line);
	void calculateLine_singlefile(const string &filepath);
	void setNoteString();	
	vector<string>	_filepaths;	
	static unsigned long _emplines;//空行
	static unsigned long _notelines;//注释行
	static unsigned long _codelines;//有效代码行	
	noteString _notestr;
};

SourceLine.cpp

#include "SourceLine.h"
#include <exception>
#include <fstream>
#include <boost/algorithm/string.hpp>
unsigned long SourceLine::_codelines = 0;
unsigned long SourceLine::_emplines = 0;
unsigned long SourceLine::_notelines = 0;
mutex emplineMutex;
mutex codelineMutex;
mutex notelineMutex;
unsigned long SourceLine::GetTotallines()
{
	unsigned long ret = GetEmptylines() + GetNotelines() + GetValidCodelines();
	return ret;
}

unsigned long SourceLine::GetEmptylines()
{
	mutex::scoped_lock lock(emplineMutex);
	return _emplines;
}

unsigned long SourceLine::GetNotelines()
{
	mutex::scoped_lock lock(notelineMutex);
	return _notelines;
}

unsigned long SourceLine::GetValidCodelines()
{
	mutex::scoped_lock lock(codelineMutex);
	return _codelines;
}

 void SourceLine::incLine(linetype tp)
 {
	 switch (tp)
	 {
	 case EMPTY_LINE:
		 {
			 mutex::scoped_lock lock(emplineMutex);
			 ++ _emplines;
		 }
		 break;	 
	 case NOTE_MULTI_LINE:
	 case NOTE_SINGLE_LINE:
		 {
			 mutex::scoped_lock lock(notelineMutex);
			 ++ _notelines;
		 }
		 break;
	 case CODE_LINE:
		 {
			 mutex::scoped_lock lock (codelineMutex);
			 ++ _codelines;
		 }
		 break;
	 default:
		 cout<<"warning: undefined line type"<<endl;
		 break;
	 }
 }

SourceLine::SourceLine()
{
	_filepaths.clear();
	setNoteString();
}

SourceLine::SourceLine(const vector<string> & filepaths, int start, int end)
{
	try
	{
		_filepaths.assign(filepaths.begin()+start, filepaths.begin()+end);
	}	
	catch (const std::exception& e)
	{
		_filepaths.clear();
		cerr<<"error SourceLine construction"<<e.what()<<endl;
	}
	setNoteString();
}
SourceLine::~SourceLine()
{

}

void SourceLine::setNoteString()
{
	noteString notestr;
	notestr.singlenotestr = "//";
	notestr.multnote_startstr = "/*";
	notestr.multnote_endstr = "*/";
	_notestr = notestr;
}

void SourceLine::SetNoteString(const noteString& notestr)
{
	_notestr = notestr;
}

/*
	遍历所有源文件,计算行数
*/
void SourceLine::CalculateLine()
{
	vector<string>::iterator iter = _filepaths.begin();
	
	for(; iter != _filepaths.end(); iter++)
		calculateLine_singlefile(*iter);
}

/*
	计算单个源文件的各种行数
*/
void SourceLine::calculateLine_singlefile(const string &filepath)
{
	ifstream fin;
	string line;
	linetype type;
	int pos;
	fin.open(filepath.c_str(), ios::in);
	if(!fin) {
		cerr<<"error open "<<filepath<<endl;
		return;
	}

	do 
	{
		getline(fin,line);
		type = analyisLine(line);

		/*
			处理“/*xsfa*/ /*dd*”多行注释的不规范写法
		*/
		while(type == NOTE_MULTI_LINE) {
			while(!contains(line, _notestr.multnote_endstr) && !fin.eof()) {
				getline(fin,line);
				incLine(NOTE_MULTI_LINE);
			}			
			if((pos = line.find(_notestr.multnote_endstr)) == string::npos) {
				cerr<<"error,invalid mutipleNote line in file."<<filepath<<endl;
				return;
			}
			
			line = line.substr(pos+(_notestr.multnote_endstr).length());
			trim(line);
			if(contains(line, _notestr.multnote_startstr))
				type = NOTE_MULTI_LINE;
			else 
				type = NOTYPE_LINE;
		}//while(type == NOTE_MULTI_LINE)

	} while (!fin.eof());	
}

/*
	处理每一行,一行分为三种情况:
	1.空行
	2.以有效代码开头的判为代码行
	3.以//或/*开头的判为注释行   
*/
linetype SourceLine::analyisLine(string& linestr)
{
	linetype type;
	trim(linestr);
	if(0 == linestr.length())
		type = EMPTY_LINE;
	else {
		if(starts_with(linestr, _notestr.singlenotestr/*"//"*/) || starts_with(linestr, _notestr.multnote_startstr/*"/*"*/))
			type = NOTE_SINGLE_LINE;
		else
			type = CODE_LINE;
	}
	incLine(type);
	if(contains(linestr, _notestr.multnote_startstr/*"/*"*/))
		type = NOTE_MULTI_LINE;
	return type;
}

main.cpp

#include "SourceLine.h"
#include "FileTravel.h"
#include <sstream>
#include <list>

// 命令行参数:文件夹 线程数
int main(int argc, char* argv[])
{
	if(argc < 3) {
		cout<<"please intput a directory  and a number as parameters."<<endl;
		return 0;
	}
	// 1. 源文件所在的路径
	FileTravel ft(argv[1]);
	vector<string> filepaths;
	vector<string> filtertypes;
	
	//2. 自定义代码源文件类型,如c++的.cpp或java的.java	

	filtertypes.push_back(".h");
	filtertypes.push_back(".cpp");
	filtertypes.push_back(".hpp");
	filtertypes.push_back(".c");

	ft.SetFilterTypes(filtertypes);
	//3. 获取所有源文件路径
	ft.GetSubfiles(filepaths);
	int filecount = filepaths.size();

	if(filecount <= 0){
		cout<<"no subfiles"<<endl;
		return 0;
	}

	// 4.使用命令行参数指定的多线程数量
	stringstream ss;
	int threadnum = -1;	
	ss << argv[2];	
	ss >> threadnum;	
	if(threadnum <= 0 )
		threadnum = 1;
	
	int interval = (interval = filecount/threadnum)?interval:1;
	thread_group grp;
	list<SourceLine*> listsl;
	/*
	5. 每个线程处理span个源文件
	*/
	for(int i=0,j=0; i<threadnum && j<filecount; i++) {
		int span = (i==(threadnum-1) && (filecount-j>interval))?(filecount-j):interval;
		SourceLine *sl =  new SourceLine(filepaths, j, j + span);
		/*
			此处可以通过sl->SetNoteString();设置编程语言的注释,默认是c++的注释 
		*/
		j += span;
		grp.add_thread(new boost::thread(boost::bind(&SourceLine::CalculateLine, sl)));
		listsl.push_back(sl);
	}

	grp.join_all();
	for(list<SourceLine*>::iterator listIter = listsl.begin(); listIter != listsl.end(); listIter++) {
		if(*listIter != NULL) {
			delete *listIter;
			*listIter = NULL;
		}
	}
	cout<<"empty lines:"<<SourceLine::GetEmptylines()<<endl
		<<"note lines:"<<SourceLine::GetNotelines()<<endl
		<<"code lines:"<<SourceLine::GetValidCodelines()<<endl
		<<"total lines:"<<SourceLine::GetTotallines()<<endl;	

	char c;
	cin>>c;
}

vs2010和g++编译通过。欢迎使用,发送bug.