个人项目开发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; //高级统计状态标识结构体- 数据统计类
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 }; - 总功能类设计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内各个路径进行代码统计与信息输出即可,简单的迭代就可以实现。
总结
前期的准备工作非常重要,知识学习与储备、类似的程序框架的参考、算法的评估、按需求设计功能模块等等,都是为中期代码开发提供方向与实现基础的,不重视前期工作的话在中期代码开发与后期的测试、文档撰写方面要吃很大的亏,代码体现的思路与实现方式也难以简洁优雅。这次实践中我就是吃了这个亏,导致在具体代码实现时做了很多前期需要做的工作,流程很乱,效率不高。
注意待统计代码文件的编码类型,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#在实践项目中的综合运用过程.这些项目从作者的学生时代写起,到项目经理结束,一直贯穿于作者 ...
随机推荐
- 如何用Python批量发现互联网“开放”摄像头
现在无论家用还是公司使用摄像头越来越多,但是安全性又如何呐?今天我来说说几款比较常用的摄像头,并且使用python如何批量检查弱口令. 第一个“海康威视”: 前段时间爆出海康威视的摄像头存在默认弱口令 ...
- Android安全机制介绍
Android的安全机制包含下面几个方面: • 进程沙箱隔离机制. • 应用程序签名机制. • 权限声明机制. • 訪问控制机制. • 进程通信机制. ...
- java开始到熟悉60
本次主题:多维数组 1,多维数组的初始话有三种:默认初始化.静态初始化.动态初始化. 这里只讲解静态初始化: 这里以二位数组为例,实际应用中,一维用得最多,二维次之,三维以及三维以上几乎很少使用,而且 ...
- 微信小程序项目实例
目前为止最全的微信小程序项目实例 2018年03月20日 11:38:28 Happy王子乐 阅读数:4188 wx-gesture-lock 微信小程序的手势密码 WXCustomSwitch ...
- 【数据结构】二叉树(c++)
头文件: #include <iostream> using namespace std; template<class Type> class Bintree; //结点类 ...
- CA与数字证书的自结
1.CA CA(Certificate Authority)是数字证书认证中心的简称,是指发放数字证书.管理数字证书.废除数字证书的权威机构. 2.数字证书 如果向CA申请数字证书的单位为A.则他申请 ...
- linux 文件记录锁详解
一: linux记录锁更恰当的称呼应该是范围锁,它是对文件某个范围的锁定. 关于记录锁的功能就是fcntl提供的第五个功能,具体使用如下: int fcntl(int fd, int cmd, str ...
- JS简单正则得到字符串中特定的值
这里就直接看演示样例吧.演示样例的目的是为了获取 a 字符串中的 c02806015 <script language="javascript"> var a = '礼 ...
- xalion三层与Web开发帖子一览表 good
使用http.sys,让delphi 的多层服务飞起来(Delphi借用http.sys充当http服务器,也就可以发送返回JSON等信息,当然浏览器也可以使用)http://www.cnblogs. ...
- FZU1977 Pandora adventure —— 插头DP
题目链接:https://vjudge.net/problem/FZU-1977 Problem 1977 Pandora adventure Accept: 597 Submit: 2199 ...