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. 如何用Python批量发现互联网“开放”摄像头

    现在无论家用还是公司使用摄像头越来越多,但是安全性又如何呐?今天我来说说几款比较常用的摄像头,并且使用python如何批量检查弱口令. 第一个“海康威视”: 前段时间爆出海康威视的摄像头存在默认弱口令 ...

  2. Android安全机制介绍

    Android的安全机制包含下面几个方面:      • 进程沙箱隔离机制.      • 应用程序签名机制.      • 权限声明机制.      • 訪问控制机制.      • 进程通信机制. ...

  3. java开始到熟悉60

    本次主题:多维数组 1,多维数组的初始话有三种:默认初始化.静态初始化.动态初始化. 这里只讲解静态初始化: 这里以二位数组为例,实际应用中,一维用得最多,二维次之,三维以及三维以上几乎很少使用,而且 ...

  4. 微信小程序项目实例

    目前为止最全的微信小程序项目实例 2018年03月20日 11:38:28 Happy王子乐 阅读数:4188   wx-gesture-lock  微信小程序的手势密码 WXCustomSwitch ...

  5. 【数据结构】二叉树(c++)

    头文件: #include <iostream> using namespace std; template<class Type> class Bintree; //结点类 ...

  6. CA与数字证书的自结

    1.CA CA(Certificate Authority)是数字证书认证中心的简称,是指发放数字证书.管理数字证书.废除数字证书的权威机构. 2.数字证书 如果向CA申请数字证书的单位为A.则他申请 ...

  7. linux 文件记录锁详解

    一: linux记录锁更恰当的称呼应该是范围锁,它是对文件某个范围的锁定. 关于记录锁的功能就是fcntl提供的第五个功能,具体使用如下: int fcntl(int fd, int cmd, str ...

  8. JS简单正则得到字符串中特定的值

    这里就直接看演示样例吧.演示样例的目的是为了获取 a 字符串中的 c02806015 <script language="javascript"> var a = '礼 ...

  9. xalion三层与Web开发帖子一览表 good

    使用http.sys,让delphi 的多层服务飞起来(Delphi借用http.sys充当http服务器,也就可以发送返回JSON等信息,当然浏览器也可以使用)http://www.cnblogs. ...

  10. FZU1977 Pandora adventure —— 插头DP

    题目链接:https://vjudge.net/problem/FZU-1977  Problem 1977 Pandora adventure Accept: 597    Submit: 2199 ...