一个cmd快捷小工具的开发

学习笔记 专栏收录该内容
4 篇文章 0 订阅

原因

本意是想通过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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LINE 1024

typedef 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#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”,直接将字符串赋值给字符指针会有警告。
  • 指针是第一次接触学习,所以犯了挺多错误。
  • 没有优化内存回收。

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

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 1024 设计师:白松林 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值