「这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战」。
前言:
经常做某种类型的项目的同学,一定会碰到下面的问题,统计代码(代码/注释)行数,整理品质数据。
还不只是统计,还需要写一堆报告,有 BUG或缺陷 的话,还得写什么直接原因、根本原因、其它地方有没有同样问题、临时解决方案、长期解决方案、预防方案、加强教育之类的。
有时候,真的会折腾到吐。
人在江湖,事情还是要做的,所以效率为王。
先从统计代码行数下手吧。网上也有很多统计代码行数的插件或工具,只是有的时候可能没法儿定制。
于是用 golang 试着写了一个简单的代码行数统计命令行工具。
相关代码:hellogo/step at main · bettersun/hellogo (github.com)
golang版代码统计命令行工具
简单功能点:
- 可自定义注释规则
- 可设定需要忽略统计的目录和文件
- 可设定需要统计的代码文件类型
- 可统计单文件,也可统计指定目录下的目标文件
- 统计结果输出文本或者json,使用 json 是考虑后面可以加上 GUI。 输出 Excel 有时间再写。
统计规则
- 注释定义文件中定义各种类型代码文件的注释规则,相同注释规则的代码文件类型合并到一起。
注释规则中包含单行注释规则,多行注释规则。 - 按行读取代码文件,每行代码按照注释规则的正则表达式来检查是单行注释,还是多行注释,还是代码。
统计流程
- 命令行运行程序,指定参数,可指定多个,也可混合指定目录和文件。
- 读取程序配置文件的内容,根据配置规则,获取参数目录的代码文件(包括参数指定的文件)
- 按照统计规则进行统计,统计完后加一个汇总处理。
- 输出统计结果到文件(文件名和文件类型可在配置文件中指定,现仅支持输出到文本或 json )。
外部文件
注释定义文件
文件名固定:define.yaml
例:
# C风格注释
- fileExtension:
- .c
- .cpp
- .cs
- .dart
- .go
- .php
- .java
- .js
# 单行注释
singleLine:
- //
# 多行注释开始(多行有多个标志的情况,相同位置的开始标志和结束标志必须配对)
multiLineStart:
- /*
# 多行注释结束
multiLineEnd:
- '*/'
# Python
- fileExtension:
- .py
singleLine:
- #
multiLineStart:
- "'''"
- '"""'
multiLineEnd:
- "'''"
- '"""'
# CSS
- fileExtension:
- .css
singleLine:
-
multiLineStart:
- "/*"
multiLineEnd:
- '*/'
# VB/VBS
- fileExtension:
- .vb
- .vbs
singleLine:
- "'"
multiLineStart:
-
multiLineEnd:
-
程序配置文件
文件名固定:config.yaml
例:
# 统计目标文件扩展名(.+小写,多个用逗号连接,不指定则统计忽略目录和忽略文件之外的所有文件)
#codeFileExt: .java,.c,.go,.yaml, , ,,
codeFileExt:
# 结果文件类型
# 0: txt 1:json
resultFileType: 3
# 结果文件名(不含扩展名)
resultFileName: step_result
# 忽略目录(多个用逗号连接)
ignorePath: .git,.svn,.idea, , ,,
# 忽略文件(多个用逗号连接)
ignoreFile: .gitignore,.DS_Store, , ,,
结构体
注释定义
// CommentDefine 代码注释定义
type CommentDefine struct {
// 扩展名(多个)
FileExtension []string `yaml:"fileExtension"`
// 单行注释标志
SingleLine []string `yaml:"singleLine"`
// 多行注释开始
MultiLineStart []string `yaml:"multiLineStart"`
// 多行注释结束
MultiLineEnd []string `yaml:"multiLineEnd"`
}
配置
// Config 程序的配置文件
type Config struct {
// 统计目标文件扩展名(.+小写,多个用逗号连接)
CodeFileExt string `yaml:"codeFileExt"`
// 忽略目录(多个用逗号连接)
IgnorePath string `yaml:"ignorePath"`
// 忽略文件(多个用逗号连接)
IgnoreFile string `yaml:"ignoreFile"`
// 结果文件类型
// 0: txt 1:json
ResultFileType string `yaml:"resultFileType"`
// 结果文件名(不含扩展名)
ResultFileName string `yaml:"resultFileName"`
}
代码行数统计相关
// CommentRegExp 代码注释统计用正则表达式
type CommentRegExp struct {
// 扩展名
FileExtension string
// 有单行注释
HasSingleLineMark bool
// 有多行注释
HasMultiLineMark bool
// 空行正则表达式
RegExEmptyLine *regexp.Regexp
// 单行注释正则表达式
RegExSingleLine []*regexp.Regexp
// 写在单行的多行注释正则表达式 即使用多行注释开始和多行注释结束来写的一行注释
RegExSingleLineStartEnd []*regexp.Regexp
// 多行注释开始正则表达式
RegExMultiLineStart []*regexp.Regexp
// 多行注释结束正则表达式
RegExMultiLineEnd []*regexp.Regexp
}
// StepInfo 代码行数信息
type StepInfo struct {
CommentDefined bool `json:"commentDefined"` // 存在注释配置 true:存在 false:不存在
Counted bool `json:"counted"` // 已统计标志 true:已统计 false:未统计
ExInfo string `json:"exInfo"` // 扩展信息
File string `json:"file"` // 文件全路径
FileName string `json:"fileName"` // 文件名
TotalStep int `json:"totalStep"` // 总行数
EmptyLineStep int `json:"emptyLineStep"` // 空行数
CommentStep int `json:"commentStep"` // 注释行数
SourceStep int `json:"sourceStep"` // 代码行数
ValidStep int `json:"validStep"` // 有效行数(注释+代码)
}
// StepSummary 代码行数信息汇总
type StepSummary struct {
StepInfo []StepInfo `json:"stepInfo"` // 代码行数统计结果
CountedFileCount int `json:"fileCount"` // 统计文件总数
FlatFile []string `json:"flatFile"` // 无注释定义文件
UnCountedFile []string `json:"unCountedFile"` // 未统计文件
TotalStep int `json:"totalStep"` // 总行数
EmptyLineStep int `json:"emptyLineStep"` // 空行总行数
CommentStep int `json:"commentStep"` // 注释总行数
SourceStep int `json:"sourceStep"` // 代码总行数
ValidStep int `json:"validStep"` // 有效总行数(注释+代码)
}
正则表达式字符串转正则表达式
多行注释开始结束例:
start := Escape(def.MultiLineStart[i])
end := Escape(def.MultiLineEnd[i])
cmtRegExp.RegExSingleLineStartEnd[i] =
regexp.MustCompile(`^[\s]*(` + start + `).*(` + end + `)[\s]*$`)
需要先转义一下,再转换成正则表达式
转义函数:
// Escape 转义
func Escape(sRegExp string) string {
v := sRegExp
v = strings.ReplaceAll(v, `*`, `\*`)
v = strings.ReplaceAll(v, `/`, `\/`)
return v
}
这里只转义了 * 和 / 。
主要统计逻辑
if !isMultiLineComment { // 不是多行注释
if cmtRegExp.RegExEmptyLine.MatchString(line) { //空行
emptyLineStep++
} else if _, isMatchSingle := MatchIn(line, cmtRegExp.RegExSingleLine); cmtRegExp.HasSingleLineMark && isMatchSingle { // 单行注释
commentStep++
} else if _, isMatchSingleStartEnd := MatchIn(line, cmtRegExp.RegExSingleLineStartEnd); cmtRegExp.HasMultiLineMark && isMatchSingleStartEnd { // 多行注释标志开始结束的单行注释
commentStep++
} else if matchIndex, isMatch = MatchIn(line, cmtRegExp.RegExMultiLineStart); cmtRegExp.HasMultiLineMark && isMatch { // 多行注释 开始
commentStep++
isMultiLineComment = true
}
} else if cmtRegExp.HasMultiLineMark { // 有多行注释
if isMultiLineComment {
if cmtRegExp.RegExEmptyLine.MatchString(line) { //多行注释里的空行
emptyLineStep++
} else {
//多行注释
commentStep++
//多行注释结束
if cmtRegExp.RegExMultiLineEnd[matchIndex].MatchString(line) { //多行注释 结束
isMultiLineComment = false
}
}
}
}
空行和单行注释的判断很简单,多行注释,需要先在开始时加一个多行注释开始的标志,多行注释结束时再计算注释行数,并把相关标志复原。
还有一个小问题没解决,像下面这种情况:
/* print Hello, World */ printf("Hello, World!\n");
单行内容里,同时有多行注释开始和多行注释结束,最后还有代码的情况,正则表达式验证的地方还需要再改一下。
除了压缩后的代码文件,极少有这种情况,所以暂时还没修改。
也没有充分测试,应该还有其它BUG,欢迎指正。
运行命令
各参数间用空格隔开
Mac:
./main /xx/project/hello /xx/go/test.go /xx/yy/zz.yaml /xx/bs
Win:
main.exe D:\xx\project\hello D:\xx\go\test.go E:\xx\yy\zz.yaml E:\xx\bs
今天的文章golang版代码行数统计命令行工具分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/17409.html