个人项目开发PSP实践-MyWCprj
MyWCprj.exe
1. What is MyWCprj.exe?
wc
是linux下一个非常好用的代码统计小工具,可以通过 -c
、-w
、-l
等选项分别进行对指定文件的代码字符数、词组数、行数统计。
应学校软件工程课程的PSP个人项目开发实践需要,特此尝试自己写一个用c++实现的wc
(MyWCprj.exe
)。
该程序仅能确保统计.c .cpp .h文件代码数的正确性。
语法
MyWCprj.exe [parameter] [fileOrDir_name1] [fileOrDir_name2] ……
功能参照
MyWCprj.exe -c file.c //输出文件 file.c 的字符数
MyWCprj.exe -w file.c //输出文件 file.c 的词的数目
MyWCprj.exe -l file.c //输出文件 file.c 的行数
MyWCprj.exe -a file.c //输出文件 file.c
MyWCprj.exe -s fileDir //递归处理目录fileDir及其子目录中代码文件,无其他参数时仅列举目录fileDir及其子目录中代码文件(.c .cpp .h),可配合一个或多个 -c -w -l -a 一起处理。
MyWCprj.exe -l file1.c file2.c file3.c //输出file1.c、file2.c、file3.c的行数
MyWCprj.exe -s -l DirPath1 DirPath2 DirPath3 //递归输出DirPath1、DirPath2、DirPath3目录及其子目录下所有代码文件的行数
测试示例
参考中典型的源代码文件 CommonSrcCode.cpp:
- 1 #include"WChead.h"
- 2
- 3
- 4 int main(int argc,char *argv[])
- 5 {
- 6 /*cout << "ar num=" << argc << endl;
- 7 for (int i = 0; i < argc; i++)
- 8 {
- 9 cout << "No." << i << " ";
- 10 cout << "ar =" << argv[i] << endl;
- 11 }
- 12 system("PAUSE");*/
- 13
- 14 if (argc < 2)
- 15 {
- 16 cout << "" << endl;
- 17 return -1;
- 18 }
- 19
- 20 class_WordCal myWC;
- 21 myWC.workMain(argc, argv);
- 22
- 23 //system("PAUSE");
- 24 return 0;
- 25 }
测试:
- # 运行环境:windows 10 pro,ver 1803,powershell
- # 使用 dir 命令,可看出 I:\TmpCode 下的示例代码文件
- PS ..\MyWCprj\Debug> dir I:\TmpCode
- Directory: I:\TmpCode
- Mode LastWriteTime Length Name
- ------
- #典型的源代码文件 CommonSrcCode.cpp
- -a---- 9/14/2018 9:34 AM 384 CommonSrcCode.cpp
- #空代码文件 EmptyCode.cpp
- -a---- 9/14/2018 7:29 PM 0 EmptyCode.cpp
- #仅有字符'a'的代码文件 Onechar.cpp
- -a---- 9/14/2018 7:32 PM 1 Onechar.cpp
- #仅有字符串“#pragma once”的代码文件 Oneline.cpp
- -a---- 9/14/2018 7:34 PM 12 Oneline.cpp
- #仅有字符串“#define” 的代码文件 Oneword.cpp
- -a---- 9/14/2018 7:32 PM 7 Oneword.cpp
- # 使用 .\MyWCprj.exe -s -w -c -l -a I:\TmpCode 组合
- # 可看出 MyWCprj.exe 递归输出当前目录下的代码文件对应信息
- PS ..\MyWCprj\Debug> .\MyWCprj.exe -s -w -c -l -a I:\TmpCode
- list_size: 5
- No.0 I:\TmpCode\CommonSrcCode.cpp:
- The Num of chars: 360 I:\TmpCode\CommonSrcCode.cpp
- The Num of words: 45 I:\TmpCode\CommonSrcCode.cpp
- The Num of Lines: 24 I:\TmpCode\CommonSrcCode.cpp
- The Num of Effective lines: 8 I:\TmpCode\CommonSrcCode.cpp
- The Num of Empty lines: 9 I:\TmpCode\CommonSrcCode.cpp
- The Num of Comment lines: 8 I:\TmpCode\CommonSrcCode.cpp
- No.1 I:\TmpCode\EmptyCode.cpp:
- The Num of chars: 0 I:\TmpCode\EmptyCode.cpp
- The Num of words: 0 I:\TmpCode\EmptyCode.cpp
- The Num of Lines: 0 I:\TmpCode\EmptyCode.cpp
- The Num of Effective lines: 0 I:\TmpCode\EmptyCode.cpp
- The Num of Empty lines: 1 I:\TmpCode\EmptyCode.cpp
- The Num of Comment lines: 0 I:\TmpCode\EmptyCode.cpp
- No.2 I:\TmpCode\Onechar.cpp:
- The Num of chars: 1 I:\TmpCode\Onechar.cpp
- The Num of words: 1 I:\TmpCode\Onechar.cpp
- The Num of Lines: 0 I:\TmpCode\Onechar.cpp
- The Num of Effective lines: 1 I:\TmpCode\Onechar.cpp
- The Num of Empty lines: 0 I:\TmpCode\Onechar.cpp
- The Num of Comment lines: 0 I:\TmpCode\Onechar.cpp
- No.3 I:\TmpCode\Oneline.cpp:
- The Num of chars: 12 I:\TmpCode\Oneline.cpp
- The Num of words: 2 I:\TmpCode\Oneline.cpp
- The Num of Lines: 0 I:\TmpCode\Oneline.cpp
- The Num of Effective lines: 1 I:\TmpCode\Oneline.cpp
- The Num of Empty lines: 0 I:\TmpCode\Oneline.cpp
- The Num of Comment lines: 0 I:\TmpCode\Oneline.cpp
- No.4 I:\TmpCode\Oneword.cpp:
- The Num of chars: 7 I:\TmpCode\Oneword.cpp
- The Num of words: 1 I:\TmpCode\Oneword.cpp
- The Num of Lines: 0 I:\TmpCode\Oneword.cpp
- The Num of Effective lines: 1 I:\TmpCode\Oneword.cpp
- The Num of Empty lines: 0 I:\TmpCode\Oneword.cpp
- The Num of Comment lines: 0 I:\TmpCode\Oneword.cpp
2. PSP实践
PSP2.1表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 430 | 120 |
Estimate | 估计这个任务需要多少时间 | 30 | 15 |
Development | 开发 | 400 | 300 |
Analysis | 需求分析 (包括学习新技术) | 150 | 45 |
Design Spec | 生成设计文档 | 20 | 40 |
Design Review | 设计复审 (和同事审核设计文档) | 20 | 60 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范)划 | 10 | 85 |
Design | 具体设计 | 30 | 120 |
Code Review | 代码复审 | 30 | 60 |
Test | 测试(自我测试,修改代码,提交修改) | 30 | 120 |
PReporting | 报告 | 110 | 50 |
Test Report | 测试报告 | 30 | 60 |
Size Measurement | 计算工作量 | 20 | 40 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 60 | 70 |
合计 | 计划 | 430 | 680 |
3. 分析思路
由于曾经有过在linux上逆向分析二进制程序的经验,对于ltrace
、wc
、objdump
、ptrace
、gdb
等二进制分析与调试工具有一定的了解与使用经历,所以对于wc
功能也有一定的了解。
于是乎,去GNU
找WC
命令行工具的C源码,简洁且优雅,在性能与可读性上有非常好的平衡。据其分析之,于是就以C++的方式实现了-c
、-w
、-l
的功能,并初步封装入c++自定义的类class_WordCal
中。
对于-a
功能,要求分析出待统计代码文件的空行、注释行、有效代码行。先是与室友沟通,结果很天马行空,基于字符判断的分析模式利用代码实现很困难且可读性不佳,于是在google中查阅与编译原理、代码预处理相关的网页与资料,结果在csdn论坛中获得启发。依然是源码注释行统计.请进来看看,感谢之至!。可利用状态机的思想:
- 假定一开始是非注释状态,读入一个字符,如果不是/,保持非注释状态继续读入字符,
- 如果字符是/,读入下一个字符,如果不是*,保持非注释状态,如果是*,进入注释状态;
- 在注释状态,类似的读入字符,如果不是*,保持注释状态继续读入字符,
- 如果字符是*,读入下一个字符,如果不是/,保持注释状态,如果是/,退出注释状态进入非字符状态。
于是自己依据该思想创建了一个结构体:
- 1 typedef struct {
- 2 bool bEffective; //有效行标识
- 3 bool bComm1; // "//" 注释行标识
- 4 bool bComm2; // "/**/" 注释行标识
- 5 }AllStatus; //高级统计状态标识结构体
具体的功能思路的代码实现见Github代码有。
至于-s
功能,由于对windows的文件操作不算太熟悉,于是在网上查找目录递归遍历的思路,基本上就是调用诸如_findfirst
、_findnext
的库函数,逐个获取目录下的子目录或文件的路径并存之于对应的string
类的vector
,遇到子目录则对子目录路径进行递归遍历。如此反复,最终可获得一个关于所有子目录路径的vector<string>
,和关于所有代码文件路径的vector<string>
。然后根据每一个代码文件路径实现指定的分析与信息输出功能即可。难点在于如何实现递归遍历上,这对于提升编程能力与启发编程思想来说是一个很好的实践。
4. 大体设计
高级统计(-a)状态结构体
AllStatus:
- 1 typedef struct {
- 2 bool bEffective; //有效行标识
- 3 bool bComm1; // "//" 注释行标识
- 4 bool bComm2; // "/**/" 注释行标识
- 5 }AllStatus; //高级统计状态标识结构体
- 1 typedef struct {
- 数据统计类
class_data
:- 1 class class_Data
- 2 {
- 3 public:
- 4 int iNumOfChar; //存储当前文件的字符数
- 5 int iNumOfWord; //存储当前文件的单词数
- 6 int iNumOfLine; //存储当前文件的行数
- 7 int iNumOfEffectiveLine; //存储当前文件的有效代码行数
- 8 int iNumOfEmptyLine; //存储当前文件的空行数
- 9 int iNumOfCommentLine; //存储当前文件的注释行数
- 10 string strTmpRowStr; //临时存储刚从文件读入的一行代码
- 11 AllStatus allStatus; //高级统计状态机
- 12
- 13 class_Data()
- 14 {
- 15 iNumOfChar = 0;
- 16 iNumOfWord = 0;
- 17 iNumOfLine = 0;
- 18 iNumOfEffectiveLine = 0;
- 19 iNumOfEmptyLine = 0;
- 20 iNumOfCommentLine = 0;
- 21 allStatus.bComm1 = false;
- 22 allStatus.bComm2 = false;
- 23 allStatus.bEffective = false;
- 24 allStatus.bEmpty = false;
- 25 }
- 26
- 27 ~class_Data()
- 28 {
- 29
- 30 }
- 31
- 32 };
- 1 class class_Data
- 总功能类设计class_WordCal:
- 1 class class_WordCal : public class_Data
- 2 {
- 3 public:
- 4 bool bOpChar; //字符统计输出开关
- 5 bool bOpLine; //行数统计输出开关
- 6 bool bOpWord; //词组统计输出开关
- 7 bool bOpRecursion; //递归统计开关
- 8 bool bOpAll; //更复杂数据统计开关
- 9 int iFileNameIndex; //存储文件名的stringVector中带操作的文件名下标
- 10 ifstream tmpifs; //存储当前打开文件的文件读操作类
- 11 vector<string> vStrFileOrDirName; //存储当前文件名的stringVector
- 12 vector<string> vStrCodeFilePath; //存储文件或目录路径的stringVector
- 13 string strFilePath; //存储当前文件的路径
- 14
- 15
- 16 void Option(int argc, char *argv[]); //功能选择函数
- 17 void Display(int Index); //统计输出函数
- 18 void StringSkip(string s, int &pos); //当s中存在' ' '\t' '\n' '\r'时,将pos置于该字符之后
- 19 void myFileFind(string DirPath); //对传入目录路径下的所有代码文件(.c .cpp .h) 录入至 vStrCodeFilePath 中
- 20 void DirList(vector<string> &vStr, const string strPath); //从strPath指定的路径中递归查找代码文件,并录入至vStrCodeFilePath中
- 21
- 22 int getword(FILE *fp); //从输入流中获取下一个词组,读取至文件结尾或异常情况出现时返回0,返回1则为其他情况
- 23 Status SetIfstream(vector<string> vStr, int Index); //设置打开文件的文件读操作类
- 24 //Status CountFromFile(int Index); //(旧代码,不兼容-s统计功能)对vStrFileOrDirName中第Index个文件名对应文件进行代码统计(-l -w -c)
- 25 //Status CountFromFileAll(int Index); //(旧代码,不兼容-s统计功能)对vStrFileOrDirName中第Index个文件名对应文件进行高级代码统计(-a)
- 26
- 27 Status CountFromDir(int Index); //对vector<string>中第Index个目录下的所有代码文件进行递归统计(-s)
- 28 Status CountFromFile(vector<string> vStr, int Index);//对vector<string>中第Index个文件名对应文件进行代码统计(-l -w -c)
- 29 Status CountFromFileAll(vector<string> vStr, int Index);//对vector<string>中第Index个文件名对应文件进行高级代码统计(-a)
- 30 void Display(vector<string> vStr, int Index); //vector<string>中第Index个文件名输出统计函数
- 31 Status EmptyCheck(string s, int pos); //空行检查与统计
- 32
- 33 void CountMain(); //统计功能入口
- 34 void workMain(int argc, char *argv[]); //类主功能函数入口
- 35
- 36 };
- 1 class class_WordCal : public class_Data
5. 关键功能伪码
1. -c -w -l :
- 1 wc_basic_function(FileName)
- 2 {
- 3 File *fp = fopen(FileName, "r");
- 4 char c;
- 5 bool bEnd = false;
- 6 while(bEnd != true)
- 7 {
- 8 while ((c = getc(fp)) != EOF)
- 9 {
- 10 if (isalpha(c)) //调用stdarg.h中的isalpha(),判断是否为词组,是则词组数++;
- 11 {
- 12 iNumOfWord++;
- 13 break;
- 14 }
- 15 iNumOfChar++;
- 16 if ((c) == '\n')
- 17 {
- 18 iNumOfLine++;
- 19 }
- 20 }
- 21 while (c != EOF) //由于词组判断完后上一个循环结束,此循环将继续统计行数及字符数。
- 22 {
- 23 iNumOfChar++;
- 24 if ((c) == '\n')
- 25 {
- 26 iNumOfLine++;
- 27 }
- 28 if (!isalpha(c))
- 29 {
- 30 break;
- 31 }
- 32 c = getc(fp);
- 33 }
- 34 bEnd = (c != EOF);
- 35 }
- 36 }
2. -a:
- 1 function_GetFileAllInfo()
- 2 {
- 3 Status tmpStatus;
- 4 allStatus.bComm1 = allStatus.bComm2 = allStatus.bEffective = false; //高级统计状态机初始化
- 5 iNumOfEffectiveLine = iNumOfEmptyLine = iNumOfCommentLine = 0; //代码行数、空行数、注释行数置零初始化
- 6 while (!tmpifs.eof())
- 7 {
- 8 pos = 0;
- 9 allStatus.bEffective = false;
- 10 getline(tmpifs, strTmpRowStr); //读取文件中的一行代码
- 11
- 12 StringSkip(strTmpRowStr, pos);
- 13
- 14 tmpStatus = EmptyCheck(strTmpRowStr, pos); //空行检查与统计
- 15 if (en_true == tmpStatus)
- 16 {
- 17 continue;
- 18 }
- 19 else if (en_fail == tmpStatus)
- 20 {
- 21 return en_fail;
- 22 }
- 23
- 24 while (1)
- 25 {
- 26 if (false == allStatus.bComm2
- 27 && '/' == strTmpRowStr[pos]
- 28 && '/' == strTmpRowStr[pos + 1]) //读取到"//"的情况
- 29 {
- 30 if (false == allStatus.bEffective)
- 31 {
- 32 iNumOfCommentLine++;
- 33 }
- 34 else
- 35 {
- 36 iNumOfEffectiveLine++;
- 37 }
- 38 break;
- 39 }
- 40
- 41 if (false == allStatus.bComm2
- 42 && '}' == strTmpRowStr[pos]
- 43 && '/' == strTmpRowStr[pos + 1]
- 44 && '/' == strTmpRowStr[pos + 2]) //读取到"}//"的情况
- 45 {
- 46 if (false == allStatus.bEffective)
- 47 {
- 48 iNumOfCommentLine++;
- 49 }
- 50 else
- 51 {
- 52 iNumOfEffectiveLine++;
- 53 }
- 54 break;
- 55 }
- 56
- 57 if (false == allStatus.bComm2
- 58 && '/' == strTmpRowStr[pos]
- 59 && '*' == strTmpRowStr[pos + 1]) //读取到"/*"的情况
- 60 {
- 61 pos += 2;
- 62 allStatus.bComm2 = true;
- 63 continue;
- 64 }
- 65 if (true == allStatus.bComm2) // "/**/"的过滤处理
- 66 {
- 67 if ('*' == strTmpRowStr[pos] && '/' == strTmpRowStr[pos + 1]) //读到"*/"时的状态处理
- 68 {
- 69 pos++;
- 70 allStatus.bComm2 = false;
- 71 iNumOfCommentLine++;
- 72 }
- 73 else if ('\0' == strTmpRowStr[pos]) //读取至行末时的处理
- 74 {
- 75 if (true == allStatus.bEffective) //若此时为有效代码状态,则代码行数++。例如 printf("hello\n"); /*this is a hello print*/
- 76 {
- 77 iNumOfEffectiveLine++;
- 78 }
- 79 else //否则,此处代表纯注释行,则注释行数++
- 80 {
- 81 iNumOfCommentLine++;
- 82 }
- 83 break;
- 84 }
- 85 pos++;
- 86 continue;
- 87 }
- 88
- 89
- 90 if ('\0' == strTmpRowStr[pos]) //运行至行末
- 91 {
- 92 if (allStatus.bEffective == false)
- 93 {
- 94 if (allStatus.bComm1 || allStatus.bComm2) //纯注释行
- 95 {
- 96 ++iNumOfCommentLine;
- 97 }
- 98 }
- 99 else if (allStatus.bEffective == true)
- 100 {
- 101 ++iNumOfEffectiveLine;
- 102 }
- 103 break;
- 104 }
- 105 pos++;
- 106 allStatus.bEffective = true;
- 107 }
- 108 }
- 109 tmpifs.close();
- 110 }
3. -s:
递归算法伪码:
- 1 class cal{
- 2 ...
- 3 vector<string> vFilePath;
- 4 vector<string> vDirPath;
- 5 ...
- 6 wc_GetFile_main(string strDirPath) //递归遍历目录,并将所有代码文件路径传入FilePath中
- 7 {
- 8 string tmpStr;
- 9 DirList(vDirPath, strDirPath + "\\*"); //从strDirPath + "\\*"指定的路径中递归查找代码文件,并录入至vFilePath中;将strDirPath的子目录名传入vDirPath中。
- 10 for (i = 0; i < vDirPath.size(); ++i)
- 11 {
- 12 tmpPath = strDirPath + "\\" + vDirPath[i]; //子目录路径拼接。
- 13 myFileFind(tmpPath); //对子目录进行递归操作。
- 14 }
- 15 }
- 16
- 17 DirList(verctor<string> &vStr,string strPath) //获取当前目录下的子目录名与代码文件路径,分别录入到vStr与vFilePath中。
- 18 {
- 19 vector<string> vStrTmp;
- 20 _finddata_t fileDir;
- 21 long lfDir = _findfirst(strPath.c_str(), &fileDir);
- 22 int tmpPos;
- 23
- 24 if (-1l == lfDir) //尚未实现具体的文件类型识别,仅进行_findfirst是否成功的判断
- 25 {
- 26 cout << "DirList: "<< strPath <<" File or dir not found!" << endl;
- 27 }
- 28 else
- 29 {
- 30 string tmp = strPath;
- 31 tmp.erase(tmp.size() - 1, 1);//去除路径中的'*'
- 32 do
- 33 {
- 34 string FileOrDirName(fileDir.name);
- 35 if (FileOrDirName.find('.') == -1) //目录则将目录名传入vStrTmp中
- 36 {
- 37 vDirPath.push_back(fileDir.name);
- 38 }
- 39 else if(
- 40 ((tmpPos = FileOrDirName.find(".cpp")) != -1 && FileOrDirName[tmpPos + 4] == '\0') //精确匹配.cpp字串
- 41 || ((tmpPos = FileOrDirName.find(".c")) != -1 && FileOrDirName[tmpPos + 2] == '\0') //精确匹配.c字串
- 42 || ((tmpPos = FileOrDirName.find(".h")) != -1 && FileOrDirName[tmpPos + 2] == '\0') //精确匹配.h字串
- 43 )//代码文件则将其路径传入vStrCodeFilePath中
- 44 {
- 45 vFilePath.push_back(tmp + FileOrDirName);
- 46 }
- 47 } while (_findnext(lfDir, &fileDir) == 0);
- 48 }
- 49 _findclose(lfDir);
- 50 vStr = vStrTmp;
- 51 }
- 52 ...
- 53 };
在获得到所有代码文件路径的vector<string> vFilePath后,逐一对vFilePath内各个路径进行代码统计与信息输出即可,简单的迭代就可以实现。
总结
前期的准备工作非常重要,知识学习与储备、类似的程序框架的参考、算法的评估、按需求设计功能模块等等,都是为中期代码开发提供方向与实现基础的,不重视前期工作的话在中期代码开发与后期的测试、文档撰写方面要吃很大的亏,代码体现的思路与实现方式也难以简洁优雅。这次实践中我就是吃了这个亏,导致在具体代码实现时做了很多前期需要做的工作,流程很乱,效率不高。
注意待统计代码文件的编码类型,utf-8在此程序中表现良好。曾在测试环节中遇到对unicode编码的文件进行统计,算法失败地一塌糊涂,统计结果与实际也相差甚远。在接近有一个半小时的时间里都是在debug,试图在算法上找错误的根源,无果。最终发现是unicode编码下宽字符串的问题,换用utf-8后表现良好。
此次的时间较赶,感觉代码质量还不高,找机会整理好思路,按PSP的流程进行代码重构。
个人项目开发PSP实践-MyWCprj的更多相关文章
- git项目开发版本控制实践
linux和bsd: 第一, bsd, berkeley software distribution, 伯克利软件套装, 是最开始的unix是开放的, 然后berkeley对unix进行了修改, 形成 ...
- WinForm/MIS项目开发之中按钮级权限实践
一.前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适合市 ...
- Android 实践项目开发 总结
Android 实践项目开发 总结 课程:移动平台应用开发实践 班级:201592 姓名:杨凤 学号:20159213 成绩:___________ 指导老师:娄嘉鹏 ...
- C#项目开发实践前言
曾经没有做过项目开发实现解说,都是在工作过程其中,自动学习,查找资料,由于在曾经的公司就我一人在做c#WinForm开发,所以,有时候在公司培训会上,我也会为新的员工进行过一些简单的项目解说,基于在培 ...
- [转]基于Starling移动项目开发准备工作
最近自己趁业余时间做的flash小游戏已经开发得差不多了,准备再完善下ui及数值后,投放到国外flash游戏站.期间也萌生想法,想把游戏拓展到手机平台.这两天尝试了下,除去要接入ane接口的工作,小游 ...
- 使用Yeoman快速启动AngularJS项目开发
本博客停止更新,请访问新个人博客:owenchen.net 前言 博客迁移到了BAE上,http://owenchen.net/,以后的文章会首发在自己的博客上,随后在博客园发布. 很久没有写文章了, ...
- atitit.微信项目开发效率慢的一些总结
atitit.微信项目开发效率慢的一些总结 #---理念问题..这个是最大的问题.. 要有专人提升开发效率才好.. #---没有一个好的开发方法体系.... ini deve 法. fell asd+ ...
- [转]Android开发最佳实践
——欢迎转载,请注明出处 http://blog.csdn.net/asce1885 ,未经本人同意请勿用于商业用途,谢谢—— 原文链接:https://github.com/futurice/and ...
- 第002篇 深入体验C#项目开发(一)
网上摘来的简介: <深入体验C#项目开发>通过10个综合实例的实现过程,详细讲解了C#在实践项目中的综合运用过程.这些项目从作者的学生时代写起,到项目经理结束,一直贯穿于作者 ...
随机推荐
- Mysql数据库中CURRENT_TIMESTAMP和ON UPDATE CURRENT_TIMESTAMP区别
如图所示,mysql数据库中,当字段类型为timestamp时,如果默认值取CURRENT_TIMESTAMP,则在insert一条记录时,end_time的值自动设置为系统当前时间,如果勾选了 ON ...
- sqlfairy用法
要画一个数据库的ER图,我比较懒,就想用工具来生成. 找了一下,发现一个不错的工具,名字叫sal-fairy 很强大,可以有很多用处. 其中有一个命令sqlt-graph,可以完成生成ER图. 用法如 ...
- 锤子Smartisan T1手机官方4.4.2系统内核版本号信息
从锤子smartisan T1手机官方系统EGL中获取内核版本号信息(由cofface提供): I/Adreno-EGL( 816): <qeglDrvAPI_eglInitialize:41 ...
- python—networkx:依据图的权重绘图
首先输入边和边的权重,随后画出节点位置.依据权重大小划分实边和虚边 #coding:utf-8 #!/usr/bin/env python """ An example ...
- go test test & benchmark
开发程序其中很重要的一点是测试,我们如何保证代码的质量,如何保证每个函数是可运行,运行结果是正确的,又如何保证写出来的代码性能是好的,我们知道单元测试的重点在于发现程序设计或实现的逻辑错误,使问题及早 ...
- 数据存储 --《高性能JavaScript》
1.数据存储的方式 1.字面量 2.变量 3.数组项 4.对象成员 2.各自的性能特点 1.访问字面量和局部变量的速度最快,访问数组项和对象成员相对较慢 2.由于局部变量在作用域链的起始位置,因此访问 ...
- flask-本地线程-请求上下文补充
context(上下文)是flask里面非常好的设计,使用flask需要非常理解应用上下文和请求上下文这两个概念 本地线程 本地线程(thread local)希望不同的线程对于内容的修改只在线程内部 ...
- Mono 和 .NET Core比翼双飞
大家好,今天给大家分享.NET 蓝图之下的Mono和.NET Core 话题,微软在Build 2019 大会上给.NET 做了一个五年规划,所以分享的主题就是<Mono和.NET Core 比 ...
- eclipse debug configurations arguments指定文件路径参数
1 eclipse debug configurations arguments指定文件路径参数 使用绝对路径,但是这个文件必须要放在该project的源码路径的外面才行,否则eclipse不认这个文 ...
- __sizeof__()
https://bugs.python.org/issue2898 https://bugs.python.org/file10353/footprint.patch Index: Python/sy ...