MyWCprj.exe

Github仓库地址

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 、wcobjdumpptracegdb等二进制分析与调试工具有一定的了解与使用经历,所以对于wc功能也有一定的了解。

于是乎,去GNUWC命令行工具的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. 大体设计

  1. 高级统计(-a)状态结构体AllStatus:

    1 typedef struct {
    2 bool bEffective; //有效行标识
    3 bool bComm1; // "//" 注释行标识
    4 bool bComm2; // "/**/" 注释行标识
    5 }AllStatus; //高级统计状态标识结构体
  2. 数据统计类 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 };
  3. 总功能类设计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 };

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内各个路径进行代码统计与信息输出即可,简单的迭代就可以实现。

总结

  1. 前期的准备工作非常重要,知识学习与储备、类似的程序框架的参考、算法的评估、按需求设计功能模块等等,都是为中期代码开发提供方向与实现基础的,不重视前期工作的话在中期代码开发与后期的测试、文档撰写方面要吃很大的亏,代码体现的思路与实现方式也难以简洁优雅。这次实践中我就是吃了这个亏,导致在具体代码实现时做了很多前期需要做的工作,流程很乱,效率不高。

  2. 注意待统计代码文件的编码类型,utf-8在此程序中表现良好。曾在测试环节中遇到对unicode编码的文件进行统计,算法失败地一塌糊涂,统计结果与实际也相差甚远。在接近有一个半小时的时间里都是在debug,试图在算法上找错误的根源,无果。最终发现是unicode编码下宽字符串的问题,换用utf-8后表现良好。

  3. 此次的时间较赶,感觉代码质量还不高,找机会整理好思路,按PSP的流程进行代码重构。

个人项目开发PSP实践-MyWCprj的更多相关文章

  1. git项目开发版本控制实践

    linux和bsd: 第一, bsd, berkeley software distribution, 伯克利软件套装, 是最开始的unix是开放的, 然后berkeley对unix进行了修改, 形成 ...

  2. WinForm/MIS项目开发之中按钮级权限实践

    一.前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适合市 ...

  3. Android 实践项目开发 总结

      Android 实践项目开发 总结 课程:移动平台应用开发实践  班级:201592  姓名:杨凤  学号:20159213 成绩:___________       指导老师:娄嘉鹏       ...

  4. C#项目开发实践前言

    曾经没有做过项目开发实现解说,都是在工作过程其中,自动学习,查找资料,由于在曾经的公司就我一人在做c#WinForm开发,所以,有时候在公司培训会上,我也会为新的员工进行过一些简单的项目解说,基于在培 ...

  5. [转]基于Starling移动项目开发准备工作

    最近自己趁业余时间做的flash小游戏已经开发得差不多了,准备再完善下ui及数值后,投放到国外flash游戏站.期间也萌生想法,想把游戏拓展到手机平台.这两天尝试了下,除去要接入ane接口的工作,小游 ...

  6. 使用Yeoman快速启动AngularJS项目开发

    本博客停止更新,请访问新个人博客:owenchen.net 前言 博客迁移到了BAE上,http://owenchen.net/,以后的文章会首发在自己的博客上,随后在博客园发布. 很久没有写文章了, ...

  7. atitit.微信项目开发效率慢的一些总结

    atitit.微信项目开发效率慢的一些总结 #---理念问题..这个是最大的问题.. 要有专人提升开发效率才好.. #---没有一个好的开发方法体系.... ini deve 法. fell asd+ ...

  8. [转]Android开发最佳实践

    ——欢迎转载,请注明出处 http://blog.csdn.net/asce1885 ,未经本人同意请勿用于商业用途,谢谢—— 原文链接:https://github.com/futurice/and ...

  9. 第002篇 深入体验C#项目开发(一)

    网上摘来的简介:        <深入体验C#项目开发>通过10个综合实例的实现过程,详细讲解了C#在实践项目中的综合运用过程.这些项目从作者的学生时代写起,到项目经理结束,一直贯穿于作者 ...

随机推荐

  1. Solidworks如何绘制装饰螺纹线

    1 插入-注解,装饰螺纹线   2 绘制装饰螺纹线,选择螺纹的边线,标准选择ISO,下面可以选择的范围就确定了(M6的孔,只能选择M8的螺纹或者M10的螺纹),画好之后在3D图中并没有明确的螺纹样式 ...

  2. CSS样式布局入门介绍,非常详尽

    转载自:http://wenboxz.com/archives/try-css-layout.html/

  3. webpack2 详解

    1.安装 npm install webpack -g npm install webpack -save-dev 2.编辑配置文件 // 引入 path var path=require('path ...

  4. goang Receiver & interface

    package main import ( "fmt" ) type Pointer struct { x string } func (this *Pointer) PrintX ...

  5. Java 递归解决 &quot;仅仅能两数相乘的计算器计算x^y&quot; 问题

    /** * 求一个数的乘方 * 求x^y,y是一个正整数. 设计算器仅仅能计算两数相乘,不能一次计算n个数相乘. * 知:2^5=(2^2)^2*2; 2^6=(2^2)^3=((4)^2)*4; 2 ...

  6. Expression Tree 学习笔记(一)

    大家可能都知道Expression Tree是.NET 3.5引入的新增功能.不少朋友们已经听说过这一特性,但还没来得及了解.看看博客园里的老赵等诸多牛人,将Expression Tree玩得眼花缭乱 ...

  7. iGrimaceVX3.0和1.44在线源手机直接安装教程

    [第一步] 先安装好三个组件设备必须是苹果越狱好后 确定6点几跟7点几的版本号才干够 首先打开cydia 选开发人员 以下 点软件源 点右上角编辑  1加入源 apt.25pp.com和IG包下载源a ...

  8. Android图表AChartEngine

    很多时候项目中我们需要对一些统计数据进行绘制表格,更多直观查看报表分析结果.基本有以下几种方法: 1:可以进行android api进行draw这样的话,效率比较低 2:使用开源绘表引擎,这样效率比较 ...

  9. firefox浏览器和IE

    http://blog.csdn.net/pipisorry/article/details/40899701 firefox浏览器插件 [下载地址add-ons for firefox]皮皮blog ...

  10. vmware 自动挂起

    用VMware跑虚拟机,经常会出现客户操作系统自己挂起的现象,怀疑是主机自己休眠的设置.设置之后,无效. 后来才发现不是主机休眠设置,还是应该设置客户操作系统中的休眠设置. 在客户机,控制面板  电源 ...