一个cmd快捷小工具的开发

一个cmd快捷小工具的开发

  • 原因
  • 过程
    • 语言选择
    • 工具选择
    • 需求分析
    • 学习
      • c语言命令行参数的了解学习
      • c语言读取文件的学习
      • 字符串切割的学习
      • c语言中执行dos命令
      • 字符串的连接
    • 整合
      • 大概设计
      • 整合代码
      • 配置文件示例
      • 部署
    • 放上最终效果
  • 一些问题

原因

本意是想通过cmd窗口更快的启动一些软件或者文件夹,之前使用dos命令编写一个一个的bat文件放入一个文件夹中,然后将整个文件夹放入path环境变量中,但这样每次想要添加一个新的快速启动的内容都比较麻烦,于是就想写一个小工具来实现更便捷的使用。

过程

语言选择

选择了c语言,一是用java的话不太符合快速启动的初衷(我只学过这俩),二是顺便可以复习一下已经差不多还给老师的c。

工具选择

之前跟随老师使用的vc++6.0,但前几天刚卸载,同时也想尝试一下新的工具,最后选择了Devcpp,使用下来感觉还行,主题选择挺好玩。

需求分析

/*
* 需求分析
*	在一个文件中添加文件名与路径还有自定义启动参数 
*	此程序带参数 s 启动后会输出一个列表:
*		-------------QuickStart-------------
*		ID	name	parameter	path	 
*		1   test    test     E/test/test.exe 
*		2   test1    test1     E/test/test.exe1 
*		3   test2    test2     E/test/test.exe2
*		..   ..    .....     .............. 
*	输入序号即可快速启动
*	若带别的参数如test ,则直接启动test 
*/

学习

c语言命令行参数的了解学习

c语言main函数有两个参数 int argc,char *argc[],第一个参数为命令行参数的个数+1(因为包含程序本身),不需要用户传递,第二个参数为一个字符串数组,存储传进来的参数,argv[0]为程序名。

		void main(int argc,char** argv){printf("%d\n",argc);printf("%s\n",argv[0]);printf("%s\n",argv[1]);printf("%s\n",argv[2]);}

c语言读取文件的学习

使用fopen函数来打开文件,该函数的返回值是一个FILE类型的指针,使用如:

   	FILE *fp = NULL;fp = fopen("testfile.txt", "r");

的方式来读取文件,其中r代表只读
打开方式的描述

使用fgets函数来读取每一行的内容,使用如:

		char buff[255];FILE *fp = NULL;fp = fopen("testfile.txt", "r");gets(buff,255,fp);

的方式来从文件fp中读取内容,
该操作会将255-1个字符存入字符数组buff中(减一的原因是第255个字符为自动补全的 ‘\0’),然后指向该文件的指针fp会定位于下一个字符。如果在读到255-1个字符之前碰到了换行符或者文件结尾,则会停止,其余位置补 ‘\0’。读到文件末尾该函数会返回NULL。

一个完整的读入一个文件并且按行输出的程序:

其中去掉换行符是因为我的测试文件有多行,默认每一行结尾都有一个换行符,在这里需要注意最后一行可能会没有换行符,开发时曾在这里出错。
在这里插入图片描述

字符串切割的学习

个人感觉这是整个开发过程最难的一步之一了(另一个是三维数组),不得不说Java中很方便的字符串切割后保存到数组的方法真好用。

在这里参考了一位大哥的代码:

https://blog.csdn.net/lell3538/article/details/48011135

#include 
#include 
#include 
#define MAX_LINE 1024typedef struct{char **str;size_t num;
}IString;int Split(char *src, char *delim, IString* istr)
{//src 要拆分的字符串//delim 分隔符//istr 返回拆分后的字符串数组与字符串的个数 int i;char *str = NULL, *p = NULL;(*istr).num = 1;str = (char*)calloc(strlen(src)+1,sizeof(char));if(str == NULL){return 0;}(*istr).str = (char**)calloc(1,sizeof(char *));if((*istr).str == NULL){return 0;}strcpy(str,src);p = strtok(str,delim);(*istr).str[0] = (char*)calloc(strlen(p)+1,sizeof(char));if((*istr).str[0] == NULL){return 0;}strcpy((*istr).str[0],p);for(i = 1; p = strtok(NULL,delim); i++){(*istr).num ++;(*istr).str = (char**)realloc((*istr).str,(i+1)*sizeof(char*));//首先扩充了字符串数组if((*istr).str == NULL){return 0;}(*istr).str[i] = (char*)calloc(strlen(p)+1,sizeof(char));if((*istr).str[i] == NULL){//这个地方将作者的0改为了i return 0; }strcpy((*istr).str[i],p);}free(str);str = p = NULL;return 1;}

这里是我的一些学习笔记

  • strlen(NULL)
    得不出结果,且结果不能参与运算
  • calloc(num,size)
    在内存的动态存储区中分配num个长度为size的连续空间,函数返回一个指向分配起始地址的指针,如果分配不成功,返回NULL
  • size_t
    long long unsinged int型
  • strtok(s, delim)
    从s开头开始的一个个被分割的串。当s中的字符查找到末尾时,返回NULL。
    strtok()用来将字符串分割成一个个片段。参数s指向欲分割的字符串,参数delim则为分割字符串中包含的所有字符。当strtok()在参数s的字符串中发现参数delim中包含的分割字符时,则会将该字符改为’\0’字符。在第一次调用时,strtok()必需给予参数s字符串,往后的调用则将参数s设置成NULL。每次调用成功则返回指向被分割出片段的指针。
  • realloc(要改变内存大小的指针名,新的大小),
    先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域(注意:原来指针是自动释放,不需要使用free),同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。分配失败返回NULL。
  • NULL在C++中指向0

c语言中执行dos命令

直接在代码中使用

system("dos 命令");

即可

字符串的连接

字符串的连接使用strcat方法如:

char pathf[MAX_LINE];
strcpy(pathf,(char*)"\nstart \" \" ");//使用strcpy方法为pathf初始化
//printf(pathf);//测试
strcat(pathf,path);
printf(pathf);
system(pathf);
//其中MAX_LINE为设置的常量

由于strcat方法会将连接后的字符串保存到第一个参数中,所以第一个参数的字符串需要足够空间来容纳连接好的字符串

整合

大概设计

/*
*	定义一个三维的字符数组***char,
*	这个三维数组的长度应是动态可变的 
*	将文件按行读入,每读入一行便进行字符串的切割,
*	切割符号为",";
*	将切割完成的字符串数组保存在三维数组中,
*	动态增加三维数组的长度 
*	调用时输入的数字为三维数组的下标 
*/

整合代码

#include 
#include 
#include 
#define MAX_LINE 1024
#define QSF "E://QuickStart//qsConfig.txt"typedef struct{char **str;size_t num;
}IString;typedef struct{char ***reslist; int size;
}ResList;ResList r;int Split(char *src, char *delim, IString* istr)
{
//	printf("Split invoking.........\n");//src 要拆分的字符串//delim 分隔符//istr 返回拆分后的字符串数组与字符串的个数 int i;char *str = NULL, *p = NULL;(*istr).num = 1;str = (char*)calloc(strlen(src)+1,sizeof(char));if(str == NULL){return 0;}(*istr).str = (char**)calloc(1,sizeof(char *));if((*istr).str == NULL){return 0;}strcpy(str,src);p = strtok(str,delim);(*istr).str[0] = (char*)calloc(strlen(p)+1,sizeof(char));if((*istr).str[0] == NULL){return 0;}strcpy((*istr).str[0],p);for(i = 1; p = strtok(NULL,delim); i++){(*istr).num ++;(*istr).str = (char**)realloc((*istr).str,(i+1)*sizeof(char*));if((*istr).str == NULL){return 0;}(*istr).str[i] = (char*)calloc(strlen(p)+1,sizeof(char));if((*istr).str[i] == NULL){//这个地方将作者的0改为了i return 0; }strcpy((*istr).str[i],p);}str = p = NULL;return 1;}int init(ResList* r){//	printf("init invoking.........\n");(*r).size = 0;IString splres;//修改1 将 IString* splres;改为直接声明 char buff[MAX_LINE];FILE* fp = NULL;int len,i,j,k;if(NULL == (fp = fopen(QSF,"r"))){perror("fail to read");exit(1);}for(i = 1; fgets(buff,MAX_LINE,fp) != NULL; i++){//	puts(buff);len = strlen(buff);if(buff[len-1] == '\n'){//考虑到可能文件的最后一行并没有换行,所以需要做一个判断 buff[len-1] = '\0';//去掉换行 }Split(buff,(char*)",",&splres);//修改2 传入的时候取地址 (*r).reslist = (char***)realloc((*r).reslist,(i+1)*sizeof(char**));//增加一个抽屉 (*r).reslist[i] = (char**)calloc(3,sizeof(char*)); //每个抽屉申请三个格子 for(j = 0; j < 3; j ++){//给每个格子赋值
//			printf((*splres).str[j]);
//			printf("\n"); (*r).reslist[i][j] = splres.str[j];//修改3 直接取属性 
//			printf((*r).reslist[i][j]);
//			printf("\n") ;} (*r).size ++;} return 0;
}int executeQs(char* path){char pathf[MAX_LINE];strcpy(pathf,(char*)"\nstart \" \" ");//printf(pathf);strcat(pathf,path);printf(pathf);system(pathf);return 0;
}//参数为空的情况下
//输入id,
//通过id获得path,然后执行
int parameterIsNull(){char* path; printf("%-10s%-15s%-15s%-50s\n\n","ID","Name","Parameter","Path");for(int i = 1; i < r.size+1; i ++){printf("%-10d%-15s%-15s%-50s",i,r.reslist[i][0],r.reslist[i][1],r.reslist[i][2]);printf("\n");} int id;printf("%s","\nPlease enter the ID number : ");scanf("%d",&id);if(id < 1 || id > r.size){perror("\nId number is not exist");return 0;}printf("%s","\nPlease wait a moment ...... \n");path = r.reslist[id][2];executeQs(path);	return 1;
}//参数不为空的情况下
//传入参数,通过参数查找id,
//通过id获得path,然后执行 
int parameterIsNotNull(char* parameter){char* path = (char*)"IsNotPath"; for(int i = 1; i < r.size+1; i++){if(!strcmp(r.reslist[i][1],parameter)){path = r.reslist[i][2];break;}}if(!strcmp("IsNotPath",path)){perror("Parameter is not exist");return 0;}printf("%s","\nPlease wait a moment ...... \n");executeQs(path);return 1;
}int main(int argc,char* argv[]){init(&r);if(argc == 2){char* parameter = argv[1];parameterIsNotNull(parameter);}else if(argc == 1){parameterIsNull();}else{perror("Parameter is Error");}return 0;
} 

配置文件示例

配置文件示例

部署

将配置文件取名为qsConfig.txt放入e盘的QuickStart目录下(或者直接修改代码中常量放到别的地方),将最后编译得到的程序所在目录加入path环境变量。
在修改代码中常量时,有个问题就是不能采用相对路径,因为在使用cmd启动时,会默认将cmd的当前目录当作当前路径,从而会让程序找不到文件。

放上最终效果

最终效果

一些问题

  • 刚开始打算使用计算的方式在最后得到三维数组的长度,也就是得到配置文件中的行数,但一直行不通,计算结果有问题,于是后来改用结构体来记录:读入文件循环的时候动态增加。
  • 在代码中定义字符串时会将’‘视为转义字符,如果是从文件中读取则不会将’'视为转义字符。
  • 字符串转换为字符指针 (char*)“testString”,直接将字符串赋值给字符指针会有警告。
  • 指针是第一次接触学习,所以犯了挺多错误。
  • 没有优化内存回收。

欢迎讨论,望各位大神不吝赐教


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部