[c语言期末大作业]cDitto
- 本人为大一,内容如有错误,欢迎大家的指正,谢谢。
- 如果涉及侵权等问题,也请联系本人,删除本文。
- 如果只想看代码,请移步至 三.cDitto的着手开发
-如果想看效果展示,请移步至 四.cDitto效果展示(图片) 或者最后的 “改善后的效果展示(视频)” - 本文分为两个大部分,分为前期初次完成结果,部分为后期经过老师指正修改版本(ps:仅修改控制台版本)
- 前期版本包含了初步控制台模式以及Qt模式
- 后期版本包含了改善功能的控制台模式
涉及知识点:一维数组,字符串,结构体,链表,文件操作,获取系统时间,调用第三方函数使用cmd命令,系统命令,类与方法,数据库,Qt可视化处理
文章目录
前期版本
一.功能介绍
(1)cDitto软件的背景
cDitto是根据一款出色的加强版剪切板软件ditto的功能,用c语言进行开发的。下面先简单介绍ditto软件。
①ditto软件介绍
Ditto软件是一款开源、免费的剪贴板管理工具,可以让用户在多个应用程序之间轻松复制和粘贴文本、图像和其他文件。该软件拥有丰富的功能,如搜索剪贴板历史记录、自定义快捷键、过滤重复项、同步剪贴板等。此外,Ditto还支持插件扩展和多语言界面,使其成为一个非常灵活而且易于使用的工具。
从功能和性能方面来看,Ditto软件表现出色,并且由于其开源和免费的特性,用户可以方便地获取它并享受其带来的便利。但是,对于初学者来说,可能需要一些时间来熟悉它的界面和操作方法。总体来说,Ditto软件是一个优秀的剪贴板管理工具,值得推荐给需要这种工具的用户。
下图是ditto官方宣传的软件特点
关于ditto的具体介绍可以看这篇文章:摆脱低效复制粘贴,使用 Ditto 提高效率 - 知乎 (zhihu.com)
关于ditto的使用可以看这段视频:Ditto 有历史记录功能的剪切板(文本、文件全通吃)_哔哩哔哩_bilibili
(2)cDitto开发初衷
最为大一学生,C语言大作业迫在眉睫,但脑子里实在空空如也,不知道做什么。有想过做类似学生管理系统等相似的项目,但是又觉得如果是做这个,我大概率可能会去网上找几篇相关文章进行借鉴,但我又希望可以根据自己所学的知识,凭着自己的能力去完成一个小项目,小软件的开发,所以对管理系统就先选择了放弃。
第二个想法是做游戏,对于初学编程语言的学生来讲,往往写出一个可以正常运行,且有趣的游戏,是一件十分令人感到骄傲的事情。可以去其他不学编程语言的同学面前炫耀(也可能只是我个人的想法吧,哈哈哈)。但最终这个想法还是被我选择放弃了。是因为平时也找不到一款让自己感兴趣的游戏,又因为最近真的灵感枯竭,又如何去开发一款令自己满意的游戏呢,如果连开发者自己都不满意的软件或者游戏,那么如何去说服用户满意。(有人可能会说,只是一份作业没必要较真,但我比较看重的是态度啦,对做事的负责,对他人的负责)
最终,在机缘巧合之下,在某次当CV工程师的时候(ctrl+c复制 ctrl+v粘贴),无意间按到了win+v,打开了一个从来没有见过的界面,但是当我研究了一番后,我发现它居然记录了我开机以来,所有复制粘贴的内容,这对于(一个CV工程师(bushi))经常使用电脑的用户来讲,是一个新天地,它可以高效运用剪切板,再也不用多个界面切换复制粘贴(代码了…)。当想法确定之后,便开始着手实现相关的功能。
二.开发cDitto的思路
(1)主要思路的梳理
目前肯定是无法完全实现一个像Ditto软件的一个小程序,但是可以着手去做它的一个核心内容–记录剪切板的历史记录。
想要实现cDitto关于记录剪切板的历史记录,我就需要在c语言的环境下去监控剪切板内容的改变。
一开始是想着监控键盘的ctrl+c这个热键的使用,但仔细一想,有些时候ctrl+c是无法复制一些内容的,需要右键->复制 才行。所以最后放弃了对ctrl+c的监控,转而去监控 Clipbrd(剪切板) ,比较无论是快捷键还是右键->复制,都会改变Clipbrd的内容。
其次,我需要进行一个文本操作,将记录的剪切板内容,保存至一个文件当中,用于储存剪切板的历史内容。
最后,通过读取文本输出内容,将剪切板的历史记录展现给用户看,且用户可以复制。
(2)根据思路构建细节
①关于监控Clipbrd
一开始是想调用windowsAPI去监控的,但是上网搜索相关的windowsAPI教程,都是讲关于windows窗口的开发,没找到关于Clipbrd的API及运用。
于是我又把目光转向到了利用cmd(终端)命令去调取剪切板的内容。很高兴,这个方法网上是有相关的教程。C语言执行cmd命令并获取执行结果_c执行cmd命令并返回结果_公羽向阳的博客-CSDN博客
最后选择使用上面博客的第三方函数,进行对剪切板内容的监控
ps:cmd(终端)直接输入Clipbrd是无法查看内容的,要输入的内容如下
②关于内容的储存
内容我一开始的想法是存储在txt文件当中,并通过凯撒加密与解密完成信息安全的问题。但后续发现,每次都输出所有的复制内容(我是通过读取文本的所有内容输出到控制台),会导致整体看上去观感差,使用体验不好,于是就决定用w模式添加到文本当中。
还有另外一种储存方式,就是利用数据库储存。这个方面在下面的③具体详述。
储存的内容我选择使用结构体,用于去记录复制的序号(方便排序),复制的内容,复制的时间(方便用户查找)。
形式如下(为何定义了两个结构体,会在 三.cDitto的着手开发 讲述)
typedef struct Ditto
{
int num;
string content;
string date;
}Ditto;
typedef struct Ditto2
{
int num;
char content[65535];
char time[128];
}Ditto2;
③关于多模式的开发
关于模式有两类–一种是控制台模式和Qt可视化界面模式
一种是游客和用户模式
1.用户和游客模式
首先讲解对于cDitto软件的用户和游客模式。这么区别的原因,是因为电脑有时候可能会给其他人使用,这时候cDitto软件就仅记录本次运行的复制内容,不去访问之前的数据。而对于用户,则可以根据数据库,获取到最新10条的复制记录。(具体细节如下)
这是关于Cditto的帮助文档
1.Cditto实现的功能是帮助使用者记录复制过的内容,方便用户查看之前的复制内容。
2.关于游客和用户的区别
①:游客身份进入Cditto软件,只能记录本次程序运行监听到的复制内容,不能查看之前运行程序所得到的内容。并且再程序关闭之后,下次打开也无法获取当次记录的复制内容。
②:用户身份进入Cditto软件,会与MySQL数据库进行连接,将用户每一次记录的数据都存入数据库中,方便用户查找每次运行程序所得到的复制内容。即每次用户运行得到的内容都会存储到数据库中,当下次运行程序,会访问数据库拿到之前的所有内容
3.关于Cditto功能的开发。Cditto软件目前功能仅支持记录文字内容,尚无法获取图片,视频等二进制内容。此问题是由于实现获取复制内容是利用CMD获取剪切板Clipbrd内容导致,后续会对此功能进行相关的调整与修改。如果你有相关的问题,意见请联系我的邮箱:jx178861659@163.com
2.控制台和Qt模式
控制台就是常见的黑窗模式,而Qt模式,则是对cDitto做出了相应的可视化处理,也使得这个小项目变成了可以给他人运用的。具体效果可查看 四.cDitto效果展示
三.cDitto的着手开发
(1)控制台模式
①菜单的建立
1.头文件,函数声明以及主菜单界面的设计
#include<stdio.h>
#include"DittoManager.h"
#include <time.h>
#include<string.h>
#include<windows.h>
typedef struct Ditto2
{
int num;
char content[65535];
char time[128];
}Ditto2;
//定义选择模式1后,用户输入的身份即相关密码
const char* user = "游客";
const char* Administrator = "用户";
const char* PassWord = "123456";
//用于获取系统时间函数
char* get_time();
//进入模式1函数(以游客身份进入Cditto软件)
void mode1();
//进入模式2函数(以用户身份进入Cditto软件)
//打印数据库不足十条时的结果
void print_dittos(const vector<Ditto>& dittos);
void mode2();
//查看帮助文档
void help();
//用于监控剪切板内容的第三方函数
int exec_cmd_1(char* cmd, char* result);
//用于游客查看复制内容函数
void search_content();
int main()
{
char username[128];
char choose[128];
while (1)
{
while (1)
{
printf("///欢迎使用Cditto\n");
printf("请输入相应序号选择功能\n");
printf("1.选择身份进入Cditto软件\n");
printf("2.查看帮助文档(游客和用户的区别)\n");
printf("3.退出Cditto程序\n");
printf("请输入序号进行选择: ");
scanf("%s", &choose);
if (strcmp(choose, "1") == 0)
{
while (1)
{
printf("\n");
printf("请选择身份进入软件(游客/用户,用户需要输入密码,输入3返回目录)\n");
printf("请输入选择的身份: ");
scanf("%s",username);
getchar();
if (strcmp(username, user) == 0)
{
mode1();
}
else if (strcmp(username, Administrator) == 0)
{
mode2();
}
else if (strcmp(username, "3") == 0)
{
break;
}
else
{
printf("您输入的序号不正确,请重新输入......\n");
}
printf("\n\n\n");
}
printf("\n\n\n");
}
else if (strcmp(choose, "2") == 0)
{
help();
}
else if (strcmp(choose, "3") == 0)
{
printf("欢迎下次使用");
return 0;
}
else
{
printf("您输入的序号不正确......\n");
}
printf("\n\n\n");
}
}
return 0;
}
2.游客菜单的建立
void mode1()
{
printf("欢迎使用cditto,您的身份是: 游客\n");
Ditto2* prev = NULL;
while (1)
{
char result[65535] = { 0 };
char cmd[] = "powershell -command \"Get-Clipboard\"";
exec_cmd_1(cmd, result);
// 如果剪贴板内容发生改变
if (prev == NULL || strcmp(result, prev->content) != 0)
{
//剪切板内容发生改变,打开文本写入内容
FILE* fp = fopen("F:\\c语言\\ditto\\ditto\\数据文本.txt", "w+");
if (fp == NULL)
{
printf("文件打开失败");
return;
}
Ditto2* current = (Ditto2*)malloc(sizeof(Ditto2));
current->num = prev == NULL ? 1 : prev->num + 1;
strcpy(current->content, result);
strcpy(current->time ,get_time());
//cout << current->time;
prev = current;
//将数据写入文本当中
fprintf(fp,"序号: %d\n复制内容: %s复制时间: %s",current->num,current->content,current->time);
//关闭文件,避免下面读取文件输出时出错
fclose(fp);
//输出结果
search_content();
printf("\n\n\n\n\n\n\n\n\n");
}
Sleep(500); // 可以适当休眠减少CPU占用率
}
}
3.用户菜单的建立
void mode2()
{
char password[128];
while (1)
{
printf("欢迎使用cditto,您的身份是: 用户\n");
printf("请输入密码(输入back返回上一界面):");
scanf("%s", password);
if (strcmp(password, PassWord) == 0)
{
printf("\n\n\n");
// 获取数据库中的所有记录
vector<Ditto> ret = DittoManager::GetInstance()->get_ditto();
// 如果记录数量不足10行,则打印全部记录
if (ret.size() <= 10)
{
print_dittos(ret);
}
else
{
// 获取最后10行数据并打印
int start = ret.size() - 10;
vector<Ditto> last_10;
for (int i = start; i < ret.size(); i++)
{
last_10.push_back(ret[i]);
}
print_dittos(last_10);
}
Ditto *prev = nullptr;
while (true)
{
char result[65535] = { 0 };
char cmd[] = "powershell -command \"Get-Clipboard\"";
exec_cmd_1(cmd, result);
// 如果剪贴板内容发生改变
if (prev == nullptr || strcmp(result, prev->content.c_str()) != 0)
{
// 查询数据库中最大的num值
int max_num = DittoManager::GetInstance()->get_max_ditto_num();
// 创建新的Ditto对象
Ditto *current = new Ditto();
current->num = max_num + 1;
current->content = result;
current->date = get_time();
// 插入数据到数据库中
DittoManager::GetInstance()->insert_ditto(*current);
// 将序号赋值给当前对象的num成员变量
(*current).num = max_num + 1;
// 打印新的数据,并更新prev指针
printf("序号: %d \n复制内容: %s复制时间: %s\n", (*current).num, current->content.c_str(), current->date.c_str());
prev = current;
}
Sleep(500);
}
}
else if (strcmp(password, "back") == 0)
{
break;
}
else
{
printf("您输入的密码不对,请重新输入......\n");
}
}
printf("\n\n\n");
}
②函数功能的实现
1.主要功能,获取剪切版内容
!!!ps:cmd命令中,直接输入clipbrd是无法获得剪切板内容的,现在是要输入下行命令才能实现
powershell -command "Get-Clipboard"
主要是调用了第三方的一个函数,其中的内容和写法本人也不甚了解,下面是参考的文章
C语言执行cmd命令并获取执行结果_c执行cmd命令并返回结果_公羽向阳的博客-CSDN博客
/****************************************************第三方函数的调用********************************************************************************/
//监控剪切板的第三方函数
int exec_cmd_1(char* cmd, char* result)
{
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
HANDLE h_read, h_write;
if (!CreatePipe(&h_read, &h_write, &sa, 0)) {
return 0;
}
STARTUPINFO si = { sizeof(STARTUPINFO) };
GetStartupInfo(&si);
si.wShowWindow = SW_HIDE;
si.hStdError = NULL;
si.hStdOutput = h_write;
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
PROCESS_INFORMATION pi;
if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi)) {
return -1;
}
CloseHandle(h_write);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
char buffer[1024];
DWORD bytes_read;
while (ReadFile(h_read, buffer, 1023, &bytes_read, NULL)) {
if (bytes_read == 0) {
break;
}
buffer[bytes_read] = '\0'; // 确保读取的字符串以'\0'结尾
strcat(result, buffer);
memset(buffer, 0, sizeof(buffer)); // 清空缓冲区
}
CloseHandle(h_read);
return 1;
}
/**
* @brief 通过创建进程的方式无控制台窗口执行cmd
*
* @param[in] cmd 命令
* @return 1|0|-1 成功|管道创建失败|进程创建失败
*/
int exec_cmd_2(char* cmd)
{
STARTUPINFO si = { sizeof(STARTUPINFO) }; // 此结构体用于指定新进程的主窗口特性 //si.cb = sizeof(STARTUPINFO);
GetStartupInfo(&si);
si.wShowWindow = SW_HIDE;
si.dwFlags = STARTF_USESHOWWINDOW;
PROCESS_INFORMATION pi; // 此结构包含有关新进程及其主线程的信息
if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi))
return -1;
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return 1;
}
/************************************************************************************************************************************/
2.获取系统时间函数
- 利用time库
- 先声明结构体time_t类型的rawtime用于储存时间,结构体指针struct tm的timeinfo用于对前面获取的时间进行转化为系统时间
- 利用time库获取时间,再利用localtime函数,将时间转化为系统时间
原因time() 函数会读取操作系统的时钟,然后返回自 “1970 年 1 月 1 日 00:00:00” 到当前时间所经过的秒数。因此,time() 返回的值可以用于计算两个时间之间的时间差,或者将其转换为可读格式,如年、月、日、时、分和秒等。
char* get_time()
{
time_t rawtime;//用于获取时间
struct tm* timeinfo;
time(&rawtime);
//cout << rawtime;
timeinfo = localtime(&rawtime);//将获取到的时间转化为系统实际记录下来
//printf("Current local time and date: %s", asctime(timeinfo));
return asctime(timeinfo);//利用asctime进行对时间的处理
//获取到时间后的写法(调用函数后的)
//char* time = get_time();
//cout << time;
//printf("当前时间是: %s", time);
}
3.帮助文档的函数实现
提前将帮助文档的内容储存好
内容如下
这是关于Cditto的帮助文档
1.Cditto实现的功能是帮助使用者记录复制过的内容,方便用户查看之前的复制内容。
2.关于游客和用户的区别
①:游客身份进入Cditto软件,只能记录本次程序运行监听到的复制内容,不能查看之前运行程序所得到的内容。并且再程序关闭之后,下次打开也无法获取当次记录的复制内容。
②:用户身份进入Cditto软件,会与MySQL数据库进行连接,将用户每一次记录的数据都存入数据库中,方便用户查找每次运行程序所得到的复制内容。即每次用户运行得到的内容都会存储到数据库中,当下次运行程序,会访问数据库拿到之前的所有内容
3.关于Cditto功能的开发。Cditto软件目前功能仅支持记录文字内容,尚无法获取图片,视频等二进制内容。此问题是由于实现获取复制内容是利用CMD获取剪切板Clipbrd内容导致,后续会对此功能进行相关的调整与修改。如果你有相关的问题,意见请联系我的邮箱:jx178861659@163.com
void help()
{
printf("\n\n\n\n");
//打开文件
FILE* fp = fopen("F:\\c语言\\ditto\\ditto\\帮助文档(1).txt","r");
if (fp == NULL)
{
printf("文件打开失败");
}
//依次读取字符,防止内容缺失等问题
char ch;
while (EOF != (ch = fgetc(fp)))
{
printf("%c", ch);
}
fclose(fp);
printf("\n\n\n\n");
}
4.游客查看数据(展示内容)函数
void search_content()
{
FILE* fp = fopen("F:\\c语言\\ditto\\ditto\\数据文本.txt", "r");
if (fp == NULL)
{
printf("文件打开失败");
}
//依次读取字符,防止内容缺失等问题
char ch;
while (EOF != (ch = fgetc(fp)))
{
printf("%c", ch);
}
fclose(fp);
}
③关于对接数据库
数据库学习视频,参考自:C++封装Mysql增删改查操作_哔哩哔哩_bilibili
sql命令参考自:(1条消息) 史上超强最常用SQL语句大全_小小张自由—>张有博的博客-CSDN博客
1.对接数据库的头文件(并声明方法)
#pragma once
#include<mysql.h>
#include<iostream>
#include<string>
#include<vector>
using namespace std;
typedef struct Ditto
{
int num;
string title;
string content;
string date;
}Ditto;
class DittoManager
{
DittoManager();
~DittoManager();
public:
static DittoManager* GetInstance() //单例模式
{
static DittoManager DittoManager;
return &DittoManager;
}
public:
bool insert_ditto(Ditto& t);
bool update_ditto(Ditto& t);
bool delete_ditto(int num);
vector<Ditto> get_ditto(string condition = "");
int get_max_ditto_num();
private:
MYSQL* con;
const char* host = "localhost";
const char* user = "root";
const char* pw = "jx20031002";
const char* database_name = "ditto";//数据库的名字 不是表的名字
const int port = 3306;
};
2.数据库运行文件(方法的定义)
#include "DittoManager.h"
DittoManager::DittoManager()
{
con = mysql_init(NULL);
//设置字符编码
mysql_options(con, MYSQL_SET_CHARSET_NAME, "GBK");
if (!mysql_real_connect(con, host, user, pw, database_name, port, NULL, 0))
{
exit(1);
}
}
DittoManager::~DittoManager()
{
mysql_close(con);
}
bool DittoManager::insert_ditto(Ditto& stu)//用于插入数据
{
char sql[1024];
sprintf(sql, "Insert into database_ditto (num,title,content,date) values(%d, '%s ', %s', '%s')",
stu.num, stu.title.c_str(), stu.content.c_str(), stu.date.c_str());
if (mysql_query(con, sql))
{
fprintf(stderr, "Failed to Insert data , Error : %s\n", mysql_error(con));
return false;
}
return true;
}
bool DittoManager::update_ditto(Ditto& stu)//用于根据序号,更新数据内容
{
char sql[1024];
sprintf(sql, "UPDATE database_ditto SET title = '&s', content='%s', date='%s'"
"where num = %d",
stu.title.c_str(), stu.content.c_str(), stu.date.c_str(), stu.num);
if (mysql_query(con, sql))
{
fprintf(stderr, "Failed to update data , Error : %s\n", mysql_error(con));
return false;
}
return true;
}
bool DittoManager::delete_ditto(int num)//用于根据序号删除数据库中的记录
{
char sql[1024];
sprintf(sql, "DELETE FROM database_ditto WHERE num=%d ",
num);
if (mysql_query(con, sql))
{
fprintf(stderr, "Failed to delete data , Error : %s\n", mysql_error(con));
return false;
}
return true;
}
vector<Ditto> DittoManager::get_ditto(string condition)//用于打印数据库中的所有记录
{
vector<Ditto> stuList;
char sql[1024];
sprintf(sql, "SELECT * FROM database_ditto %s ", condition.c_str());
if (mysql_query(con, sql))
{
fprintf(stderr, "Failed to selete data , Error : %s\n", mysql_error(con));
return {};
}
MYSQL_RES* res = mysql_store_result(con);
MYSQL_ROW row;
while ((row = mysql_fetch_row(res)))
{
Ditto stu;
stu.num = atoi(row[0]);
stu.title = row[1];
stu.content = row[2];
stu.date = row[3];
stuList.push_back(stu);
}
return stuList;
}
int DittoManager::get_max_ditto_num()//用于获取数据库中序号最大的存在
{
const char* query = "SELECT MAX(num) FROM database_ditto";
int max_num = -1;
if (mysql_query(con, query) == 0)
{
MYSQL_RES* res = mysql_store_result(con);
MYSQL_ROW row = mysql_fetch_row(res);
if (row != nullptr && row[0] != nullptr)
{
max_num = atoi(row[0]);
}
mysql_free_result(res);
}
else
{
printf("get_max_ditto_num failed: %s\n", mysql_error(con));
}
return max_num;
}
(2)总代码
关于头文件和运行文件查看上面的 ③关于对接数据库 的内容
#include<stdio.h>
#include"DittoManager.h"
#include <time.h>
#include<string.h>
#include<windows.h>
typedef struct Ditto2
{
int num;
char content[65535];
char time[128];
}Ditto2;
//定义选择模式1后,用户输入的身份即相关密码
const char* user = "游客";
const char* Administrator = "用户";
const char* PassWord = "123456";
//用于获取系统时间函数
char* get_time();
//进入模式1函数(以游客身份进入Cditto软件)
void mode1();
//进入模式2函数(以用户身份进入Cditto软件)
//打印数据库不足十条时的结果
void print_dittos(const vector<Ditto>& dittos);
void mode2();
//查看帮助文档
void help();
//用于监控剪切板内容的第三方函数
int exec_cmd_1(char* cmd, char* result);
//用于游客查看复制内容函数
void search_content();
int main()
{
char username[128];
char choose[128];
while (1)
{
while (1)
{
printf("///欢迎使用Cditto\n");
printf("请输入相应序号选择功能\n");
printf("1.选择身份进入Cditto软件\n");
printf("2.查看帮助文档(游客和用户的区别)\n");
printf("3.退出Cditto程序\n");
printf("请输入序号进行选择: ");
scanf("%s", &choose);
if (strcmp(choose, "1") == 0)
{
while (1)
{
printf("\n");
printf("请选择身份进入软件(游客/用户,用户需要输入密码,输入3返回目录)\n");
printf("请输入选择的身份: ");
scanf("%s",username);
getchar();
if (strcmp(username, user) == 0)
{
mode1();
}
else if (strcmp(username, Administrator) == 0)
{
mode2();
}
else if (strcmp(username, "3") == 0)
{
break;
}
else
{
printf("您输入的序号不正确,请重新输入......\n");
}
printf("\n\n\n");
}
printf("\n\n\n");
}
else if (strcmp(choose, "2") == 0)
{
help();
}
else if (strcmp(choose, "3") == 0)
{
printf("欢迎下次使用");
return 0;
}
else
{
printf("您输入的序号不正确......\n");
}
printf("\n\n\n");
}
}
return 0;
}
//利用time库获取时间,再利用localtime函数,将时间转化为系统时间
//原因time() 函数会读取操作系统的时钟,然后返回自 "1970 年 1 月 1 日 00:00:00" 到当前时间所经过的秒数。因此,time() 返回的值可以用于计算两个时间之间的时间差,或者将其转换为可读格式,如年、月、日、时、分和秒等。
char* get_time()
{
time_t rawtime;//用于获取时间
struct tm* timeinfo;
time(&rawtime);
//cout << rawtime;
timeinfo = localtime(&rawtime);//将获取到的时间转化为系统实际记录下来
//printf("Current local time and date: %s", asctime(timeinfo));
return asctime(timeinfo);
//获取到时间后的写法(调用函数后的)
//char* time = get_time();
//cout << time;
//printf("当前时间是: %s", time);
}
void mode1()
{
printf("欢迎使用cditto,您的身份是: 游客\n");
Ditto2* prev = NULL;
while (1)
{
char result[65535] = { 0 };
char cmd[] = "powershell -command \"Get-Clipboard\"";
exec_cmd_1(cmd, result);
// 如果剪贴板内容发生改变
if (prev == NULL || strcmp(result, prev->content) != 0)
{
//剪切板内容发生改变,打开文本写入内容
FILE* fp = fopen("F:\\c语言\\ditto\\ditto\\数据文本.txt", "w+");
if (fp == NULL)
{
printf("文件打开失败");
return;
}
Ditto2* current = (Ditto2*)malloc(sizeof(Ditto2));
current->num = prev == NULL ? 1 : prev->num + 1;
strcpy(current->content, result);
strcpy(current->time ,get_time());
//cout << current->time;
prev = current;
//将数据写入文本当中
fprintf(fp,"序号: %d\n复制内容: %s复制时间: %s",current->num,current->content,current->time);
//关闭文件,避免下面读取文件输出时出错
fclose(fp);
//输出结果
search_content();
printf("\n\n\n\n\n\n\n\n\n");
}
Sleep(500); // 可以适当休眠减少CPU占用率
}
}
void print_dittos(const vector<Ditto>& dittos)
{
for (int i = 0; i < dittos.size(); i++)
{
printf("序号: %d \n复制内容: %s \n复制时间: %s\n", dittos[i].num, dittos[i].content.c_str(), dittos[i].date.c_str());
printf("\n\n");
}
}
void mode2()
{
char password[128];
while (1)
{
printf("欢迎使用cditto,您的身份是: 用户\n");
printf("请输入密码(输入back返回上一界面):");
scanf("%s", password);
if (strcmp(password, PassWord) == 0)
{
printf("\n\n\n");
// 获取数据库中的所有记录
vector<Ditto> ret = DittoManager::GetInstance()->get_ditto();
// 如果记录数量不足10行,则打印全部记录
if (ret.size() <= 10)
{
print_dittos(ret);
}
else
{
// 获取最后10行数据并打印
int start = ret.size() - 10;
vector<Ditto> last_10;
for (int i = start; i < ret.size(); i++)
{
last_10.push_back(ret[i]);
}
print_dittos(last_10);
}
Ditto *prev = nullptr;
while (true)
{
char result[65535] = { 0 };
char cmd[] = "powershell -command \"Get-Clipboard\"";
exec_cmd_1(cmd, result);
// 如果剪贴板内容发生改变
if (prev == nullptr || strcmp(result, prev->content.c_str()) != 0)
{
// 查询数据库中最大的num值
int max_num = DittoManager::GetInstance()->get_max_ditto_num();
// 创建新的Ditto对象
Ditto *current = new Ditto();
current->num = max_num + 1;
current->content = result;
current->date = get_time();
// 插入数据到数据库中
DittoManager::GetInstance()->insert_ditto(*current);
// 将序号赋值给当前对象的num成员变量
(*current).num = max_num + 1;
// 打印新的数据,并更新prev指针
printf("序号: %d \n复制内容: %s复制时间: %s\n", (*current).num, current->content.c_str(), current->date.c_str());
prev = current;
}
Sleep(500);
}
}
else if (strcmp(password, "back") == 0)
{
break;
}
else
{
printf("您输入的密码不对,请重新输入......\n");
}
}
printf("\n\n\n");
}
void help()
{
printf("\n\n\n\n");
//打开文件
FILE* fp = fopen("F:\\c语言\\ditto\\ditto\\帮助文档(1).txt","r");
if (fp == NULL)
{
printf("文件打开失败");
}
//依次读取字符,防止内容缺失等问题
char ch;
while (EOF != (ch = fgetc(fp)))
{
printf("%c", ch);
}
fclose(fp);
printf("\n\n\n\n");
}
void search_content()
{
FILE* fp = fopen("F:\\c语言\\ditto\\ditto\\数据文本.txt", "r");
if (fp == NULL)
{
printf("文件打开失败");
}
//依次读取字符,防止内容缺失等问题
char ch;
while (EOF != (ch = fgetc(fp)))
{
printf("%c", ch);
}
fclose(fp);
}
/****************************************************第三方函数的调用********************************************************************************/
//监控剪切板的第三方函数
int exec_cmd_1(char* cmd, char* result)
{
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
HANDLE h_read, h_write;
if (!CreatePipe(&h_read, &h_write, &sa, 0)) {
return 0;
}
STARTUPINFO si = { sizeof(STARTUPINFO) };
GetStartupInfo(&si);
si.wShowWindow = SW_HIDE;
si.hStdError = NULL;
si.hStdOutput = h_write;
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
PROCESS_INFORMATION pi;
if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi)) {
return -1;
}
CloseHandle(h_write);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
char buffer[1024];
DWORD bytes_read;
while (ReadFile(h_read, buffer, 1023, &bytes_read, NULL)) {
if (bytes_read == 0) {
break;
}
buffer[bytes_read] = '\0'; // 确保读取的字符串以'\0'结尾
strcat(result, buffer);
memset(buffer, 0, sizeof(buffer)); // 清空缓冲区
}
CloseHandle(h_read);
return 1;
}
/**
* @brief 通过创建进程的方式无控制台窗口执行cmd
*
* @param[in] cmd 命令
* @return 1|0|-1 成功|管道创建失败|进程创建失败
*/
int exec_cmd_2(char* cmd)
{
STARTUPINFO si = { sizeof(STARTUPINFO) }; // 此结构体用于指定新进程的主窗口特性 //si.cb = sizeof(STARTUPINFO);
GetStartupInfo(&si);
si.wShowWindow = SW_HIDE;
si.dwFlags = STARTF_USESHOWWINDOW;
PROCESS_INFORMATION pi; // 此结构包含有关新进程及其主线程的信息
if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi))
return -1;
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return 1;
}
/************************************************************************************************************************************/
//实现对接数据库的函数
//int main()
//{
// //Ditto stu1{ 1, "这是复制内容1", "2023-4-27" };
// Ditto stu2{ 2, "这是复制内容2", "2023-4-27" };
//
//
// DittoManager::GetInstance()->update_ditto(stu1); // 更新,根据主变量
// DittoManager::GetInstance()->insert_ditto(stu2); // 插入
// //DittoManager::GetInstance()->delete_ditto(333333); // 删除,根据主变量
//
//
//vector<Ditto> ret = DittoManager::GetInstance()->get_ditto();
//for (auto& t : ret)
//{
// cout << "ID: " << t.num << ' ' << "Content: " << t.content << ' ' << "date: " << t.date << endl;
//}
//
//
// return 0;
//}
//
//
(3)Qt版本相关代码
先说明相关文件
①头文件总代码
1.dittomanager.h代码
#ifndef DITTOMANAGER_H
#define DITTOMANAGER_H
#include <QtSql>
#include <QStringList>
#include <QVector>
typedef struct Ditto
{
int num;
QString content;
QString date;
} Ditto;
class DittoManager
{
public:
static DittoManager* GetInstance() {
static DittoManager instance;
return &instance;
}
bool insertDitto(Ditto& ditto);
bool updateDitto(Ditto& ditto);
bool deleteDitto(int num);
QVector<Ditto> getDittos(QString condition = "");
int getMaxDittoNum();
private:
DittoManager();
QSqlDatabase m_db;
};
#endif // DITTOMANAGER_H
2.help_window.h代码
#ifndef HELP_WINDOW_H
#define HELP_WINDOW_H
#include <QMainWindow>
class help_window : public QMainWindow
{
Q_OBJECT
public:
explicit help_window(QWidget *parent = nullptr);
signals:
void helpwindowback();
};
#endif // HELP_WINDOW_H
3.mainwindow.h代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "help_window.h"
#include"youke_window.h"
#include"user_window.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
//切换窗口
//窗口1.帮助文本窗口
help_window *helpwindow = NULL;
//窗口2.游客使用界面
youke_window *youkewindow = NULL;
//窗口3.用户使用界面
user_window *userwindow = NULL;
//用户登录密码
const QString PassWord = "123456";
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
4.user_window.h代码
#ifndef USER_WINDOW_H
#define USER_WINDOW_H
#include <QMainWindow>
#include"help_window.h"
class user_window : public QMainWindow
{
Q_OBJECT
public:
explicit user_window(QWidget *parent = nullptr);
//窗口1.帮助文本窗口
help_window *helpwindow = NULL;
//重写绘图事件
void paintEvent(QPaintEvent *event);
signals:
//返回信号
void userback();
};
#endif // USER_WINDOW_H
5.youke_window.h代码
#ifndef YOUKE_WINDOW_H
#define YOUKE_WINDOW_H
#include <QMainWindow>
#include"help_window.h"
class youke_window : public QMainWindow
{
Q_OBJECT
public:
explicit youke_window(QWidget *parent = nullptr);
//窗口1.帮助文本窗口
help_window *helpwindow = NULL;
signals:
//返回信号
void youkeback();
};
#endif // YOUKE_WINDOW_H
②源文件(.cpp)总代码
1.dittomanager.cpp代码
#include "dittomanager.h"
DittoManager::DittoManager()
{
m_db = QSqlDatabase::addDatabase("QMYSQL");
m_db.setHostName("localhost");
m_db.setPort(3306);
m_db.setUserName("root");
m_db.setPassword("jx20031002");
m_db.setDatabaseName("ditto");
if (!m_db.open()) {
qDebug() << "Error: failed to connect database." << m_db.lastError();
exit(1);
}
// 创建表格
/*这段代码是用来创建一个 SQLite 数据库中的名为 "database_ditto" 的表格。该表格包含三个列:num、content 和 date。
num 列被指定为主键,也就是表格中唯一标识每行数据的列。因此,它必须是唯一的,并且不能为空。
content 和 date 列都是 TEXT 类型的,分别用于存储文本和日期数据。
CREATE TABLE IF NOT EXISTS 是 SQL 语句中用于创建表格的命令。如果已经存在同名的表格,则不会重新创建,而是跳过执行 CREATE TABLE 命令。在这种情况下,不会出现错误,程序会继续执行后面的代码。
如果创建表格失败,会输出错误信息并退出程序。*/
/*这里的 query 是 QSqlQuery 类的一个对象,用于在 SQLite 数据库中执行 SQL 查询和命令。可以通过构造函数传入一个已经打开的数据库连接对象来创建它。
在这段代码中,我们使用了 query 对象来执行 SQL 命令 CREATE TABLE IF NOT EXISTS database_ditto (...),这个命令会创建名为 database_ditto 的表格并定义其列信息。
如果 query.exec(createSql) 执行成功,则会返回 true,否则返回 false。如果执行失败,可以通过 query.lastError() 获取详细的错误信息。在这里,如果创建表格失败,就会输出错误信息并退出程序。*/
QSqlQuery query(m_db);
QString createSql = "CREATE TABLE IF NOT EXISTS database_ditto ("
"num INTEGER PRIMARY KEY,"
"content TEXT,"
"date TEXT)";
if (!query.exec(createSql)) {
qDebug() << "Error: failed to create table." << query.lastError();
exit(1);
}
}
bool DittoManager::insertDitto(Ditto& ditto)
{
QSqlQuery query(m_db);
query.prepare("INSERT INTO database_ditto (num, content, date) VALUES (:num, :content, :date)");
query.bindValue(":num", ditto.num);
query.bindValue(":content", ditto.content);
query.bindValue(":date", ditto.date);
if (!query.exec()) {
qDebug() << "Error: failed to insert data." << query.lastError();
return false;
}
return true;
}
bool DittoManager::updateDitto(Ditto& ditto)
{
QSqlQuery query(m_db);
query.prepare("UPDATE database_ditto SET content=:content, date=:date WHERE num=:num");
query.bindValue(":num", ditto.num);
query.bindValue(":content", ditto.content);
query.bindValue(":date", ditto.date);
if (!query.exec()) {
qDebug() << "Error: failed to update data." << query.lastError();
return false;
}
return true;
}
bool DittoManager::deleteDitto(int num)
{
QSqlQuery query(m_db);
query.prepare("DELETE FROM database_ditto WHERE num=:num");
query.bindValue(":num", num);
if (!query.exec()) {
qDebug() << "Error: failed to delete data." << query.lastError();
return false;
}
return true;
}
/*这段代码是 DittoManager 类的成员函数,用于从表格 database_ditto 中获取符合指定条件的数据,并将结果以 QVector<Ditto> 的形式返回。
首先,在函数内部创建了一个 QVector<Ditto> 对象 dittoList,用于存储查询结果。
然后,创建了一个 QSqlQuery 对象 query,并使用 SQL 语句 "SELECT * FROM database_ditto " + condition 查询表格中所有列的数据,并且根据传入的 condition 参数添加额外的查询条件。在这里,我们假设查询条件已经按照 SQL 语法正确地拼接到了 condition 参数中。
如果执行查询命令成功,则进入 while 循环处理查询结果中的每一行数据。
在循环内部,首先创建了一个 Ditto 对象 ditto,并且通过 query.value("num").toInt()、query.value("content").toString() 和 query.value("date").toString() 方法依次获取当前行 num、content 和 date 列的值,并分别赋给 ditto 对象的相应属性。
最后,将 ditto 对象添加到 dittoList 集合中。循环处理完毕后,将 dittoList 集合作为结果返回给调用者。
总之,这段代码的作用是从表格 database_ditto 中获取满足特定条件的数据,并将其封装为 Ditto 对象的集合返回。在实际应用中,可以通过这个函数来实现搜索、过滤和分页等功能。*/
/*QVector<Ditto> 是 Qt 框架中的一个容器类,用于存储一组元素的列表。其中 Ditto 是自定义结构体或类的类型,表示向量中要存储的数据类型。
在这段代码中,QVector<Ditto> 表示存储了若干个 Ditto 对象的列表,可以用来存储从数据库中查询到的多个 Ditto 记录。通过使用 QVector 容器类,可以方便地对这些记录进行排序、过滤、遍历等操作,是 C++ 程序员常用的数据集合之一。*/
QVector<Ditto> DittoManager::getDittos(QString condition)
{
QVector<Ditto> dittoList;
QSqlQuery query(m_db);
QString sql = "SELECT * FROM database_ditto " + condition;
if (!query.exec(sql)) {
qDebug() << "Error: failed to select data." << query.lastError();
return dittoList;
}
while (query.next())
{
Ditto ditto;
ditto.num = query.value("num").toInt();
ditto.content = query.value("content").toString();
ditto.date = query.value("date").toString();
dittoList.push_back(ditto);
}
return dittoList;
}
int DittoManager::getMaxDittoNum()
{
QSqlQuery query(m_db);
QString sql = "SELECT MAX(num) FROM database_ditto";
if (!query.exec(sql)) {
qDebug() << "Error: failed to get max num." << query.lastError();
return -1;
}
/*query.next() 方法会将查询结果集中的指针移动到下一行,如果下一行还有数据,则返回 true,否则返回 false。因此,这个条件语句的作用是判断是否有查询结果。
如果有查询结果,query.value(0) 会获取当前行第一列(即索引为 0 的列)的值,并且通过 toInt() 方法将其转换为整数类型并返回。在这里,我们假设第一列存储的是整型数据。
如果没有查询结果,return -1 会表示查询失败,并返回一个特定的标识值 -1。
通常情况下,这段代码可能是用于查询表格中某一列的最大或最小值,以及统计行数等操作。*/
if (query.next()) {
return query.value(0).toInt();
} else {
return -1;
}
}
2.help_window.cpp代码
#include "help_window.h"
#include<QMenuBar>
#include<QPushButton>
#include<QTimer>
#include<QTextEdit>
#include<QMessageBox>
#include<QFile>
help_window::help_window(QWidget *parent)
: QMainWindow{parent}
{
//配置登录主场景(选择游客或用户)
//设置固定大小
setFixedSize(700,241);
//设置图标
setWindowIcon(QIcon(":/img/Title.png"));
//设置标题
setWindowTitle("Cditto--帮助文档");
//创建菜单栏
QMenuBar *bar = new QMenuBar();
setMenuBar(bar);
//创建开始菜单
QMenu * startMenu = bar->addMenu("菜单");
//创建退出菜单项
QAction *quitAction1 = startMenu->addAction("帮助文本");
QAction *quitAction2 = startMenu->addAction("退出");
//创建返回按钮
QPushButton *backbtn = new QPushButton(this);
QPixmap pix;
pix.load(":/data/back.png");
backbtn->setFixedSize(50,50);
//backbtn->setStyleSheet("QPushButton{border:0px}");
backbtn->setIcon(pix);
backbtn->setIconSize(QSize(40,40));
backbtn->move(this->width()-backbtn->width(),this->height()-backbtn->height());
connect(backbtn,&QPushButton::clicked,[=](){
//延时返回
QTimer::singleShot(100,this,[=](){
emit this->helpwindowback();
});
});
// 创建文本框控件
QTextEdit *textEdit = new QTextEdit(this);
textEdit->setGeometry(0, 0, 700, 200);
// 打开帮助文本文件并读取内容到文本框
QFile file(":/data/help.txt");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QMessageBox::warning(this, "错误", "无法打开帮助文本文件!");
}
else
{
QTextStream in(&file);
QString text = in.readAll();
textEdit->setText(text);
file.close();
}
}
3.mainwindow.cpp代码
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QPushButton>
#include<QPixmap>
#include<QPainter>
#include<QLabel>
#include <QLineEdit>
#include<QGridLayout>
#include<QMessageBox>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//配置登录主场景(选择游客或用户)
//设置固定大小
setFixedSize(400,240);
//设置图标
setWindowIcon(QIcon(":/data/Title.png"));
//设置标题
setWindowTitle("Cditto--登录界面");
//创建菜单栏
QMenuBar *bar = new QMenuBar();
setMenuBar(bar);
//创建开始菜单
QMenu * startMenu = bar->addMenu("菜单");
//创建退出菜单项
QAction *quitAction1 = startMenu->addAction("帮助文本");
QAction *quitAction2 = startMenu->addAction("退出");
helpwindow = new help_window;
connect(quitAction1,&QAction::triggered,[=](){
this->hide();
helpwindow->setGeometry(this->geometry());
helpwindow->show();
helpwindow->activateWindow(); // 将焦点设置为用户界面
});
//监听帮助文本窗口的返回按钮的信号
connect(helpwindow,&help_window::helpwindowback,this,[=](){
this->setGeometry(helpwindow->geometry());
helpwindow->hide();
this->show();//重新显示主场景
this->activateWindow(); // 将焦点设置为用户界面
});
//监听退出按钮
connect(quitAction2,&QAction::triggered,[=](){
this->close();
});
// 创建登录界面布局
QWidget *loginWidget = new QWidget(this); // 创建一个 QWidget
QGridLayout *layout = new QGridLayout(loginWidget); // 将布局应用到这个 QWidget 上
// 创建账户标签和单行文本框
QLabel *accountLabel = new QLabel("账户:", loginWidget);
QLineEdit *accountEntry = new QLineEdit(loginWidget);
accountEntry->setText("游客/用户,如果是游客直接点击确认,用户请输入密码再确认");
layout->addWidget(accountLabel, 0, 0);
layout->addWidget(accountEntry, 0, 1);
// 创建密码标签和单行文本框
QLabel *passwordLabel = new QLabel("密码:", loginWidget);
QLineEdit *passwordEntry = new QLineEdit(loginWidget);
passwordEntry->setEchoMode(QLineEdit::Password);
layout->addWidget(passwordLabel, 1, 0);
layout->addWidget(passwordEntry, 1, 1);
// 创建确认按钮
QPushButton *confirmButton = new QPushButton("确认", loginWidget);
layout->addWidget(confirmButton, 2, 1);
// 设置布局
loginWidget->setLayout(layout);
setCentralWidget(loginWidget); // 将 QWidget 设置为 MainWindow 的 centralWidget
//实现确认按钮功能
youkewindow = new youke_window;
userwindow = new user_window;
//将两个界面的运行一开始先设置为false,只有当选择一种身份时,才开始运行其中一种模式
youkewindow->setEnabled(false);
userwindow->setEnabled(false);
//监听游客使用窗口的返回按钮的信号
connect(youkewindow,&youke_window::youkeback,this,[=](){
this->setGeometry(youkewindow->geometry());
youkewindow->hide();
youkewindow->setEnabled(false);
this->show();//重新显示主场景
this->activateWindow(); // 将焦点设置为用户界面
setDisabled(false); // 显示当前主场景窗口并将其状态设置为可用
});
//监听用户使用窗口的返回按钮的信号
connect(userwindow,&user_window::userback,this,[=](){
this->setGeometry(userwindow->geometry());
userwindow->hide();
userwindow->setEnabled(false);
this->show();//重新显示主场景
this->activateWindow(); // 将焦点设置为用户界面
setDisabled(false); // 显示当前主场景窗口并将其状态设置为可用
});
connect(confirmButton,&QPushButton::clicked,[=](){
QString account = accountEntry->text();
QString password = passwordEntry->text();
if(account == "游客"){ // 如果账户为游客
// 进入youke_window界面
QMessageBox::information(this, "提示", "欢迎您,游客!");
// 打开youke_window界面的代码
this->hide();
youkewindow->setEnabled(true);
youkewindow->setGeometry(this->geometry());
youkewindow->show();
youkewindow->activateWindow(); // 将焦点设置为用户界面
setDisabled(true); // 隐藏当前主场景窗口并将其状态设置为不可用
}
else if(account == "用户"){ // 如果账户为用户
if(password == PassWord){ // 如果密码正确
// 进入user_window界面
QMessageBox::information(this, "提示", "欢迎您,用户!");
// 打开user_window界面的代码
this->hide();
userwindow->setEnabled(true);
userwindow->setGeometry(this->geometry());
userwindow->show();
userwindow->activateWindow(); // 将焦点设置为用户界面
setDisabled(true); // 隐藏当前主场景窗口并将其状态设置为不可用
}
else{ // 如果密码错误
// 提醒用户输入密码错误
QMessageBox::warning(this, "警告", "密码错误,请重新输入!");
}
}
else{ // 如果账户既不是游客也不是用户
// 提醒使用者选择账户
QMessageBox::warning(this, "警告", "请选择账户!(游客或用户)");
}
});
}
MainWindow::~MainWindow()
{
delete ui;
}
4.user_window.cpp代码
#include "user_window.h"
#include"help_window.h"
#include<QMenuBar>
#include<QPushButton>
#include<QTimer>
#include<QTextEdit>
#include<QMessageBox>
#include<QFile>
#include<QPainter>
#include <QScrollArea>
#include <QScrollBar>
#include<QDebug>
#include<stdio.h>
#include"DittoManager.h"
#include <time.h>
#include<string.h>
#include<windows.h>
char* get_time();
int exec_cmd_1(char* cmd, char* result);
void print_dittos(const QVector<Ditto>& dittos);
user_window::user_window(QWidget *parent)
: QMainWindow{parent}
{
//配置登录主场景(选择游客或用户)
//设置固定大小
setFixedSize(380,520);
//设置图标
setWindowIcon(QIcon(":/img/Title.png"));
//设置标题
setWindowTitle("Cditto--用户界面");
//创建菜单栏
QMenuBar *bar = new QMenuBar();
setMenuBar(bar);
//创建开始菜单
QMenu * startMenu = bar->addMenu("菜单");
//创建退出菜单项
QAction *quitAction1 = startMenu->addAction("帮助文本");
QAction *quitAction2 = startMenu->addAction("退出");
// 设置菜单栏透明
bar->setStyleSheet("background: transparent;");
helpwindow = new help_window;
connect(quitAction1,&QAction::triggered,[=](){
this->hide();
helpwindow->setGeometry(this->geometry());
helpwindow->show();
helpwindow->activateWindow(); // 将焦点设置为用户界面
});
//监听帮助文本窗口的返回按钮的信号
connect(helpwindow,&help_window::helpwindowback,this,[=](){
this->setGeometry(helpwindow->geometry());
helpwindow->hide();
this->show();//重新显示主场景
this->activateWindow(); // 将焦点设置为用户界面
});
//监听退出按钮
connect(quitAction2,&QAction::triggered,[=](){
this->close();
});
//创建返回按钮
QPushButton *backbtn = new QPushButton(this);
QPixmap pix;
pix.load(":/data/back.png");
backbtn->setFixedSize(50,50);
//backbtn->setStyleSheet("QPushButton{border:0px}");
backbtn->setIcon(pix);
backbtn->setIconSize(QSize(40,40));
backbtn->move(this->width()-backbtn->width(),this->height()-backbtn->height());
//监听返回按钮
connect(backbtn,&QPushButton::clicked,[=](){
//延时返回
QTimer::singleShot(100,this,[=](){
emit this->userback();
});
});
//设置按钮样式为透明
backbtn->setStyleSheet("background-color: rgba(0, 0, 0, 0);");
// 创建一个的文本框并添加跳转到底部按钮
QScrollArea *scrollArea = new QScrollArea(this);
QTextEdit *textEdit = new QTextEdit(scrollArea);
scrollArea->setWidget(textEdit);
scrollArea->setWidgetResizable(true);
QPushButton *scrollToBottomButton = new QPushButton("跳转到底部", this);
QObject::connect(scrollToBottomButton, &QPushButton::clicked, [=](){
textEdit->verticalScrollBar()->setValue(textEdit->verticalScrollBar()->maximum());
});
// 设置滚动区域和按钮的位置和大小
textEdit->setStyleSheet("background-color: rgba(0, 0, 0, 0);");
scrollArea->setStyleSheet("background-color: rgba(0, 0, 0, 0);");
scrollArea->setGeometry(30, 50, 320, 420);
scrollToBottomButton->setGeometry(160, 480, 100, 30);
// // 创建一个透明的文本框
// QTextEdit *textEdit = new QTextEdit(this);
// textEdit->move(30, 50);
// textEdit->setFixedSize(320, 420);
// textEdit->setStyleSheet("background-color: rgba(0, 0, 0, 0);");
//功能实现
QVector<Ditto> ret = DittoManager::GetInstance()->getDittos();
// 如果记录数量不足10行,则打印全部记录
if (ret.size() <= 10)
{
for (int i = 0; i < ret.size(); i++)
{
// printf("序号: %d \n复制内容: %s \n复制时间: %s\n", dittos[i].num, dittos[i].content, dittos[i].date);
// printf("\n\n\n");
QString str1 = QString("序号: %1 \n复制内容: %2复制时间: %3\n\n\n").arg(ret[i].num).arg(ret[i].content).arg(ret[i].date);
textEdit->append(str1);
}
}
else
{
// 获取最后10行数据并打印
int start = ret.size() - 10;
for (int i = start; i < ret.size(); i++)
{
QString str3 = QString("序号: %1 \n复制内容: %2复制时间: %3\n\n\n").arg(ret[i].num).arg(ret[i].content).arg(ret[i].date);
textEdit->append(str3);
}
}
Ditto *prev = new Ditto();
nullptr 是 C++11 中引入的关键字,用于表示空指针。在 C++ 中,使用 0 或者 NULL 表示空指针时可能会出现一些问题,例如:
// int* p = NULL; // 这里使用了 NULL 表示空指针
//上述代码中,NULL 实际上被定义为整型 0,而不是一个真正的指针类型,这可能会导致一些潜在的问题。为了避免这种情况,C++11 引入了 nullptr 关键字。
QTimer *timer1 = new QTimer(this);
connect(timer1,&QTimer::timeout,[=](){
char result[65535] = { 0 };
char cmd[] = "powershell -command \"Get-Clipboard\"";
exec_cmd_1(cmd, result);
// 将char*类型的字符串转换为QString类型
QString str = QString::fromLocal8Bit(result);
// 将QString类型的字符串转换为UTF-8编码的QByteArray
QByteArray data = str.toUtf8();
// 如果剪贴板内容发生改变
if (prev == nullptr || strcmp(data, prev->content.toUtf8().data()) != 0)
{
// 查询数据库中最大的num值
int max_num = DittoManager::GetInstance()->getMaxDittoNum();
// 创建新的Ditto对象
Ditto *current = new Ditto();
current->num = max_num + 1;
current->content = data;
current->date = get_time();
// 插入数据到数据库中
DittoManager::GetInstance()->insertDitto(*current);
// 将序号赋值给当前对象的num成员变量
(*current).num = max_num + 1;
// 打印新的数据,并更新prev指针
//printf("序号: %d \n复制内容: %s复制时间: %s\n", (*current).num, current->content.c_str(), current->date.c_str());
prev->num = current->num;
prev->content=current->content;
prev->date=current->date;
QString str2 = QString("\n\n\n 序号: %1\n复制内容: %2\n复制时间: %3").arg(current->num).arg(current->content).arg(current->date);
//qDebug() << str;
textEdit->append(str2);
}
});
timer1->start(1000);
}
void user_window::paintEvent(QPaintEvent *event)
{
//加载背景
QPainter painter(this);
QPixmap pix;
pix.load(":/data/bg.png");
painter.drawPixmap(0,0,this->width(),this->height(),pix);
//加载标题
pix.load(":/res/Title.png");
painter.drawPixmap((this->width()-pix.width())*0.5,30,pix.width(),pix.height(),pix);
}
5.youke_window.cpp代码
#include "youke_window.h"
#include"help_window.h"
#include<QMenuBar>
#include<QPushButton>
#include<QMenuBar>
#include<QPushButton>
#include<QTimer>
#include<QTextEdit>
#include<QMessageBox>
#include<QFile>
#include <QScrollArea>
#include <QScrollBar>
#include<QApplication>
#include<QClipboard>
#include<QDateTime>
#include<QThread>
#include<QTextStream>
#include<stdio.h>
#include"DittoManager.h"
#include <time.h>
#include<string.h>
#include<windows.h>
typedef struct Ditto2
{
int num;
char content[65535];
char time[128];
}Ditto2;
//用于获取系统时间函数
char* get_time();
//用于监控剪切板内容的第三方函数
int exec_cmd_1(char* cmd, char* result);
youke_window::youke_window(QWidget *parent)
: QMainWindow{parent}
{
//配置游客的使用界面
//设置固定大小
setFixedSize(380,520);
//设置图标
setWindowIcon(QIcon(":/data/Title.png"));
//设置标题
setWindowTitle("Cditto--游客界面");
//创建菜单栏
QMenuBar *bar = new QMenuBar();
setMenuBar(bar);
//创建开始菜单
QMenu * startMenu = bar->addMenu("菜单");
//创建退出菜单项
QAction *quitAction1 = startMenu->addAction("帮助文本");
QAction *quitAction2 = startMenu->addAction("退出");
helpwindow = new help_window;
connect(quitAction1,&QAction::triggered,[=](){
this->hide();
helpwindow->setGeometry(this->geometry());
helpwindow->show();
helpwindow->activateWindow(); // 将焦点设置为用户界面
});
//监听帮助文本窗口的返回按钮的信号
connect(helpwindow,&help_window::helpwindowback,this,[=](){
this->setGeometry(helpwindow->geometry());
helpwindow->hide();
this->show();//重新显示主场景
this->activateWindow(); // 将焦点设置为用户界面
});
//监听退出按钮
connect(quitAction2,&QAction::triggered,[=](){
this->close();
});
//创建返回按钮
QPushButton *backbtn = new QPushButton(this);
QPixmap pix;
pix.load(":/data/back.png");
backbtn->setFixedSize(50,50);
//backbtn->setStyleSheet("QPushButton{border:0px}");
backbtn->setIcon(pix);
backbtn->setIconSize(QSize(40,40));
backbtn->move(this->width()-backbtn->width(),this->height()-backbtn->height());
//监听返回按钮
connect(backbtn,&QPushButton::clicked,[=](){
//延时返回
QTimer::singleShot(100,this,[=](){
emit this->youkeback();
});
});
// 创建一个的文本框并添加跳转到底部按钮
QScrollArea *scrollArea = new QScrollArea(this);
QTextEdit *textEdit = new QTextEdit(scrollArea);
scrollArea->setWidget(textEdit);
scrollArea->setWidgetResizable(true);
QPushButton *scrollToBottomButton = new QPushButton("跳转到底部", this);
QObject::connect(scrollToBottomButton, &QPushButton::clicked, [=](){
textEdit->verticalScrollBar()->setValue(textEdit->verticalScrollBar()->maximum());
});
// 设置滚动区域和按钮的位置和大小
scrollArea->setGeometry(30, 50, 320, 420);
scrollToBottomButton->setGeometry(160, 480, 100, 30);
//功能实现
// 周期性检测剪贴板内容是否变化并写入文件
Ditto2* prev = new Ditto2();
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, [=](){
char result[65535] = { 0 };
char cmd[] = "powershell -command \"Get-Clipboard\"";
exec_cmd_1(cmd, result);
// 如果剪贴板内容发生改变
if (prev == NULL || strcmp(result, prev->content) != 0)
{
//剪切板内容发生改变,打开文本写入内容
// QFile file("F:\\QT\\QTcode\\Qt_cditto\\youke_data.txt");//参数就是读取文件的路径
// if(!file.open(QIODevice::WriteOnly | QIODevice::Text));
Ditto2* current = (Ditto2*)malloc(sizeof(Ditto2));
current->num = prev == NULL ? 1 : prev->num + 1;
strcpy(current->content, result);
strcpy(current->time ,get_time());
//cout << current->time;
prev->num = current->num;
strcpy(prev->content,current->content);
strcpy(prev->time,current->time);
//prev = current;
//将数据写入文本当中
// 写入数据
QString str = QString("\n\n\n 序号: %1\n复制内容: %2\n复制时间: %3").arg(current->num).arg(QString::fromLocal8Bit(current->content).toUtf8()).arg(current->time);
//qDebug() << str;
textEdit->append(str);
// file.open(QIODeviceBase::ReadOnly);
// //QByteArray array = file.readAll();
// QByteArray array;
// while(!file.atEnd())
// {
// array += file.readLine();//按行读取
// }
// //将读取到的数据 放入textedit中
// //textEdit->append(codec->toUnicode(array)); //gbk格式转码
}
});
timer->start(500);
}
//利用time库获取时间,再利用localtime函数,将时间转化为系统时间
//原因time() 函数会读取操作系统的时钟,然后返回自 "1970 年 1 月 1 日 00:00:00" 到当前时间所经过的秒数。因此,time() 返回的值可以用于计算两个时间之间的时间差,或者将其转换为可读格式,如年、月、日、时、分和秒等。
//将WCHAR类型的时间字符串转换为UTF-8编码格式的char类型字符串
char* get_time()
{
static char buf[128];
time_t lt;
struct tm *local;
lt = time(NULL); ///获取相对于1970到现在的秒数
local = localtime(<); ///转为当地时间
strftime(buf, 64, "%Y-%m-%d %H:%M:%S", local);
return buf;
}
/****************************************************第三方函数的调用********************************************************************************/
//监控剪切板的第三方函数
int exec_cmd_1(char* cmd, char* result)
{
HANDLE hRead, hWrite;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
if (!CreatePipe(&hRead, &hWrite, &sa, 0))
{
return -1;
}
STARTUPINFOA si;
PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
si.hStdOutput = hWrite;
si.hStdError = hWrite;
si.wShowWindow = SW_HIDE;
if (!CreateProcessA(NULL, cmd, NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi))
{
CloseHandle(hWrite);
CloseHandle(hRead);
return -2;
}
CloseHandle(hWrite);
DWORD read_len = 0;
while (true) {
char buffer[65535] = { 0 };
if (ReadFile(hRead, buffer, sizeof(buffer), &read_len, NULL) == NULL || read_len == 0) {
break;
}
strcat(result, buffer);
}
CloseHandle(hRead);
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD exitCode;
GetExitCodeProcess(pi.hProcess, &exitCode);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return 0;
}
/************************************************************************************************************************************/
6.main.cpp代码
#include "mainwindow.h"
#include <QApplication>
#include<QSqlDatabase>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
四.cDitto效果展示
(1)控制台模式
进入程序,选择查看帮助文档效果图
选择游客身份效果图
游客身份的内容
序号2复制内容为空,是因为复制内容为图片,无法在cmd命令中显示,此问题会在后续开发中解决。
选择用户身份效果图
(首先打印数据库中,最新的十条记录,然后开始正常的记录复制内容,并存入数据库中,与游客使用相同)
(2)Qt可视化界面模式
游客模式效果图
用户模式效果图
数据库的正常录入数据
五.问题发现与改善方向
(1)存在问题
1.在使用控制台模式的时候,当进入监控剪切板内容后,无法退出设置的死循环
2.在Qt版本中,无法屏蔽游客界面和用户界面。意思就是,当程序开始运行,两个模式都会开始工作,就算进入的是游客模式,用户模式任然会记录复制内容,上传数据库。
3.在Qt版本用户模式下,点击菜单背景色为黑色,小问题会在最后修改
(2)改善方向
1.解决控制台无法退出死循环的问题。
2.完善Qt界面,后续会在Qt用户模式下,添加搜索功能。
3.在经过与老师的讨论,可以在循环当中加入用户自己的判断,用户自己判断是否记录本次复制内容,如果是,则调用函数完成复制内容的记录,并自己选择是否添加标签。
4.会继续完善控制台版本的功能,美化控制台的输出。
六.引用文献
关于ditto的具体介绍可以看这篇文章:摆脱低效复制粘贴,使用 Ditto 提高效率 - 知乎 (zhihu.com)
C语言执行cmd命令并获取执行结果_c执行cmd命令并返回结果_公羽向阳的博客-CSDN博客
Ditto 有历史记录功能的剪切板(文本、文件全通吃)_哔哩哔哩_bilibili
C++封装Mysql增删改查操作_哔哩哔哩_bilibili
(1条消息) 史上超强最常用SQL语句大全_小小张自由—>张有博的博客-CSDN博客
后期版本(控制台功能完善)
一.完善方面
-
完善了控制台之前是通过换行达到清屏效果,保证控制台的简洁。但发现界面任然不美观,后续通过系统命令
去完成程序的清屏以及控制进程的作用
system("cls")//用于控制台清屏 system("pause")//用于程序进程的控制
-
完善了第一个版本进入游客或用户功能后,进入死循环无法在做更改等问题。
-
添加了询问游客或用户是否添加保存,是否添加标题的问题
-
完善游客查找功能,查找的方法有 序号和标题。
-
完善了用户的查找功能,查找的方法有 序号,标题,内容,时间。(都是模糊查找,只要含有关键词即可)
二.相关代码改进
(1).各个界面的改进
①新函数的声明主菜单界面的改进
#include<stdio.h>
#include"DittoManager.h"
#include <time.h>
#include<string.h>
#include<windows.h>
typedef struct Ditto2
{
int num;
char title[128];
char content[65535];
char time[128];
Ditto2* next;
}Ditto2;
//定义选择模式1后,用户输入的身份即相关密码
const char* user = "游客";
const char* Administrator = "用户";
const char* PassWord = "123456";
//用于获取系统时间函数
char* get_time();
//进入模式1函数(以游客身份进入Cditto软件)
void mode1();//游客模式下的菜单界面
Ditto2 * fun1(Ditto2* prev,const char result[65535]);//当游客选择保存后进入的功能函数
Ditto2* fun1_(Ditto2* prev, const char result[65535]);//当游客选择不保存之后进入的功能函数
void youke_search(Ditto2* head);//显示游客的历史复制记录
void search_by_num(Ditto2* head, const char* key);//通过序号查找数据的功能函数
void search_by_title(Ditto2* head, const char* key);//通过标题查找数据的功能函数
//进入模式2函数(以用户身份进入Cditto软件)
void print_dittos(const vector<Ditto>& dittos);//用于打印传递过来的dittos容器的所有内容
void print_search_dittos(const vector<Ditto>& dittos);//
void print_dittos_all();//用于打印数据库所有数据的内容
void user_search_by_num(const string& keyword);//用于用户通过序号查询数据
void user_search_by_title(const string& keyword);//用于用户通过标题查询数据
void user_search_by_content(const string& keyword);//用于用户通过内容查询数据
void user_search_by_date(const string& keyword);//用于用户通过日期查询数据
Ditto* fun2(Ditto* prev, const char result[65535]);//当用户选择保存后进入的功能函数
Ditto* fun2_(Ditto* prev, const char result[65535]);//当用户选择不保存后进入的功能函数
void mode2();//用户模式下的菜单界面
void mode2_search_by_num(string key);//菜单中选择序号查询的过渡函数
void mode2_search_by_title(string key);//菜单中选择标题查询的过渡函数
void mode2_search_by_content(string key);//菜单中选择标题查询的过渡函数
void mode2_search_by_date(string key);//菜单中选择序号标题的过渡函数
//查看帮助文档
void help();
//用于监控剪切板内容的第三方函数
int exec_cmd_1(char* cmd, char* result);
//用于游客查看复制内容函数
void search_content();
//用于用户查看复制内容,之所以选择先存入文本是防止控制台因为循环反复读取导致闪烁的问题
void mode2_search_content();
int main()
{
char username[128];
char choose[128];
system("cls");
while (1)
{
system("cls");
printf("///欢迎使用Cditto\n");
printf("请输入相应序号选择功能\n");
printf("1.选择身份进入Cditto软件\n");
printf("2.查看帮助文档(游客和用户的区别)\n");
printf("3.退出Cditto程序\n");
printf("请输入序号进行选择: ");
scanf("%s", &choose);
if (strcmp(choose, "1") == 0)
{
while (1)
{
system("cls");
printf("请选择身份进入软件(游客/用户,用户需要输入密码,输入3返回目录)\n");
printf("请输入选择的身份: ");
scanf("%s",username);
getchar();
if (strcmp(username, user) == 0 || strcmp(username,"youke") == 0)
{
mode1();
}
else if (strcmp(username, Administrator) == 0 || strcmp(username, "yonghu") == 0)
{
mode2();
}
else if (strcmp(username, "3") == 0)
{
break;
}
else
{
printf("您输入的序号不正确,请重新输入......\n");
}
system("pause");
}
}
else if (strcmp(choose, "2") == 0)
{
help();
}
else if (strcmp(choose, "3") == 0)
{
//退出程序则将游客,用户本次涉及到的数据存储相关文本清空,避免数据的泄露。
FILE* fp1 = fopen("F:\\c语言\\ditto\\ditto\\数据文本.txt", "w+");
if (fp1 == NULL)
{
printf("文件打开失败");
}
fprintf(fp1, "%c",'\0');
fclose(fp1);
FILE* fp2 = fopen("F:\\c语言\\ditto\\ditto\\数据文本(用户).txt", "w+");
if (fp2 == NULL)
{
printf("文件打开失败");
}
fprintf(fp2, "%c", '\0');
fclose(fp2);
printf("欢迎下次使用");
return 0;
}
else
{
printf("您输入的序号不正确......\n");
}
system("pause");
}
return 0;
}
②游客模式界面改善
添加查询功能
void mode1()
{
char panduan[128];
printf("欢迎使用cditto,您的身份是: 游客\n");
Ditto2* head = NULL; //头节点指针,用于后续的查找功能使用
Ditto2* prev = NULL; // 前驱指针初始化为NULL
Ditto2* temp = prev; //中间过渡变量,用于游客不保存内容时,任然需要更新最新的剪切板内容,防止
//用户选择不保存后,由于程序内容没有更新而导致一直询问是否保存。
while (1)
{
system("pause");
system("cls");
printf("请选择功能:\n");
printf("1.记录模式\n");
printf("2.查找模式\n");
printf("3.退回上一级目录\n");
scanf("%s", panduan);
if (strcmp(panduan, "1") == 0)
{
system("cls");
while (1)
{
char result[65535] = { 0 };
char cmd[] = "powershell -command \"Get-Clipboard\"";
exec_cmd_1(cmd, result);
// 如果剪贴板内容发生改变
if (temp == NULL || strcmp(result, temp->content) != 0)//利用temp去比较内容
{
system("cls");
//判断是否需要记录
printf("您已复制新内容,是否保存\n");
printf("Y/y保存,Q/q返回上级目录,其余任意键取消保存\n");
// 消耗掉输入缓冲区中的换行符
while (getchar() != '\n') {}
char input = getchar();
if (input == 'Y' || input == 'y')
{
prev = fun1(prev, result);//将需要保存的新内容传递回来保存到链表中
// 如果prev为空,则说明链表为空,需要将head指向当前节点
if (head == NULL)
{
head = prev;
}
temp = prev;//更新中间变量temp的内容
}
else if (input == 'Q' || input == 'q')
{
break;
}
else
{
//当选择不保存时,不用存入链表,但仍然需要更新内容
temp = fun1_(temp, result);
}
system("cls");
//输出结果
search_content();//通过读取文本内容输出结果
Sleep(500); // 可以适当休眠减少CPU占用率
}
}
}
else if (strcmp(panduan, "2") == 0)
{
youke_search(head);
}
else if (strcmp(panduan, "3") == 0)
{
break;
}
else
{
printf("您输入的序号不对,请重新输入......");
}
}
}
③用户模式界面的改善
添加查询功能
void mode2()
{
char password[128];
while (1)
{
system("cls");
printf("欢迎使用cditto,您的身份是: 用户\n");
printf("请输入密码(输入back返回上一界面):");
scanf("%s", password);
if (strcmp(password, PassWord) == 0)
{
while (1)
{
system("cls");
// 打印菜单选项
printf("1.记录模式 \n2.查找模式 \n3.显示所有记录 \n4.删除模式 \n5.返回上级目录\n\n");
int choice;
printf("请选择功能:");
scanf("%d", &choice);
while (getchar() != '\n') {}
if (choice == 1) // 记录模式
{
system("cls");
// 获取数据库中的所有记录
vector<Ditto> ret = DittoManager::GetInstance()->get_ditto();
// 如果记录数量不足10行,则打印全部记录
if (ret.size() <= 10)
{
print_dittos(ret);
}
else
{
// 获取最后10行数据并打印
int start = ret.size() - 10;
vector<Ditto> last_10;
for (int i = start; i < ret.size(); i++)
{
last_10.push_back(ret[i]);
}
print_dittos(last_10);
}
Ditto* prev = nullptr;
while (true)
{
char result[65535] = { 0 };
char cmd[] = "powershell -command \"Get-Clipboard\"";
exec_cmd_1(cmd, result);
// 如果剪贴板内容发生改变
if (prev == nullptr || strcmp(result, prev->content.c_str()) != 0)
{
system("cls");
//判断是否需要记录
printf("您已复制新内容,是否保存\n");
printf("Y/y保存,Q/q返回上级目录,其余任意键取消保存\n");
// 消耗掉输入缓冲区中的换行符
while (getchar() != '\n') {}
char input = getchar();
if (input == 'Y' || input == 'y')
{
prev = fun2(prev, result);//选择保存,将数据插入数据库中
}
else if (input == 'Q' || input == 'q')
{
break;
}
else
{
prev = fun2_(prev, result);//选择不报错,则只更新内容即可
}
//输出结果
system("cls");
mode2_search_content();//通过文本文件防止控制台闪烁问题
}
Sleep(500);
}
}
else if (choice == 2) // 查找模式
{
system("cls");
int select;
string key;
printf("请选择搜索方式(均为模糊搜索):\n");
printf("1. 按照序号搜索\n");
printf("2. 按照标题搜索\n");
printf("3. 按照内容搜索\n");
printf("4. 按照日期搜索\n");
printf("请选择搜索方式:");
scanf("%d", &select);
switch (select) {
case 1:
printf("请输入要搜索的序号:");
cin >> key;
mode2_search_by_num(key);
break;
case 2:
printf("请输入要搜索的标题:");
cin >> key;
mode2_search_by_title(key);
break;
case 3:
printf("请输入要搜索的内容:");
cin >> key;
mode2_search_by_content(key);
break;
case 4:
printf("请输入要搜索的日期:");
cin >> key;
mode2_search_by_date(key);
break;
default:
printf("无效的选项,请重新选择。\n");
break;
}
//string keyword;
//printf("请输入要搜索的关键字:");
//scanf("%s", keyword);
//search_dittos(string(keyword));
}
else if (choice == 3) // 显示所有记录
{
system("cls");
print_dittos_all();
}
else if (choice == 4) // 删除模式
{
system("cls");
int num;
printf("请输入要删除的记录序号:");
scanf("%d", &num);
delete_ditto(num);
}
else if (choice == 5) // 返回上级目录
{
return;
}
else
{
printf("无效选择,请重新输入。\n");
}
system("pause");
}
}
else if (strcmp(password, "back") == 0)
{
break;
}
else
{
printf("您输入的密码不对,请重新输入......\n");
}
system("pause");
}
}
(2)相关函数的实现
①游客相关函数的实现
1.游客保存功能与不保存功能
//游客的保存功能实现
Ditto2 *fun1(Ditto2* prev, const char result[65535])
{
//通过文件实现保存
FILE* fp = fopen("F:\\c语言\\ditto\\ditto\\数据文本.txt", "a+");
if (fp == NULL)
{
printf("文件打开失败");
return prev;
}
Ditto2* current = (Ditto2*)malloc(sizeof(Ditto2));
current->num = prev == NULL ? 1 : prev->num + 1;
strcpy(current->content, result);
strcpy(current->time, get_time());
current->next = NULL;
// 询问用户是否需要添加标题
char panduan;
printf("是否需要添加标题?是请输入 Y/y,否则输入其他任意键:");
// 消耗掉输入缓冲区中的换行符
while (getchar() != '\n') {}
panduan = getchar();
if (panduan == 'y' || panduan == 'Y')
{
printf("请输入标题:");
scanf("%s", current->title);
}
else
{
strcpy(current->title,"无自定义标题");
}
if (prev == NULL)//如果链表为空,则将prev记录下第一条内容
{
prev = current;
}
else//如果链表不为空,则将current数据插入到链表尾部。
{
prev->next = current;
prev = current;
}
//将数据写入文件
fprintf(fp, "序号:%d\n标题:%s\n复制内容:%s\n复制时间:%s\n\n",
current->num, current->title, current->content, current->time);
fclose(fp);
//返回current,用于更新内容
return current;
}
Ditto2* fun1_(Ditto2* temp, const char result[65535])
{
Ditto2* current = (Ditto2*)malloc(sizeof(Ditto2));
current->num = 0;
strcpy(current->content, result);
strcpy(current->time, get_time());
current->next = NULL;
// 消耗掉输入缓冲区中的换行符
while (getchar() != '\n') {}
strcpy(current->title, "无自定义标题");
//返回current,用于更新内容
return current;
}
2.游客查找功能
i.游客查找界面设计
void youke_search(Ditto2* head)
{
int choice;
char key[50];
printf("请选择搜索方式:\n");
printf("1. 按照序号搜索\n");
printf("2. 按照标题搜索\n");
scanf("%d", &choice);
switch (choice) {
case 1:
printf("请输入要搜索的序号:");
scanf("%s", key);
search_by_num(head, key);
break;
case 2:
printf("请输入要搜索的标题:");
scanf("%s", key);
search_by_title(head, key);
break;
default:
printf("无效的选项,请重新选择。\n");
break;
}
}
ii.游客查找–序号
void search_by_num(Ditto2* head, const char* key)
{
Ditto2* p = head;
int num = atoi(key); // 将关键字转换成整数
while (p != NULL)
{
if (p->num == num)
{ // 如果序号匹配成功
printf("序号:%d\n标题:%s\n复制内容:%s\n复制时间:%s\n\n",
p->num, p->title, p->content, p->time);
return;
}
p = p->next;
}
printf("未找到匹配项。\n");
}
ii.游客查找–标题
void search_by_title(Ditto2* head, const char* key)
{
Ditto2* p = head;
while (p != NULL) {
if (strcmp(p->title, key) == 0) { // 如果标题匹配成功
printf("序号:%d\n标题:%s\n复制内容:%s\n复制时间:%s\n\n",
p->num, p->title, p->content, p->time);
return;
}
p = p->next;
}
printf("未找到匹配项。\n");
}
②用户相关函数的实现
1.用于打印数据库所有数据内容
// 打印所有Ditto记录的函数
void print_dittos_all()
{
vector<Ditto> ret = DittoManager::GetInstance()->get_ditto();
for (auto& t : ret)
{
cout << "ID: " << t.num << '\n' << "title: " <<t.title << '\n' << "Content: " << t.content << '\n' << "date: " << t.date << endl;
}
}
2.用于打印容器中的数据内容(主要用于数据库最新10条数据的读取与储存到文件当中)
//打印容器dittos中所有的(数据库)内容
void print_dittos(const vector<Ditto>& dittos)
{
for (int i = 0; i < dittos.size(); i++)
{
//printf("序号: %d \n标题:%s \n复制内容: %s \n复制时间: %s\n", dittos[i].num, dittos[i].title.c_str(), dittos[i].content.c_str(), dittos[i].date.c_str());
//printf("\n\n");
FILE* fp = fopen("F:\\c语言\\ditto\\ditto\\数据文本(用户).txt", "a+");
if (fp == NULL)
{
printf("文件打开失败");
}
fprintf(fp,"序号: %d \n标题:%s \n复制内容: %s \n复制时间: %s\n\n",dittos[i].num, dittos[i].title.c_str(), dittos[i].content.c_str(), dittos[i].date.c_str());
fclose(fp);
}
}
3.用于打印搜索后,容器(数据)中的内容
//打印搜索结果
void print_search_dittos(const vector<Ditto>& dittos)
{
for (int i = 0; i < dittos.size(); i++)
{
//printf("序号: %d \n标题:%s \n复制内容: %s \n复制时间: %s\n", dittos[i].num, dittos[i].title.c_str(), dittos[i].content.c_str(), dittos[i].date.c_str());
//printf("\n\n");
printf("序号: %d \n标题:%s \n复制内容: %s \n复制时间: %s\n\n", dittos[i].num, dittos[i].title.c_str(), dittos[i].content.c_str(), dittos[i].date.c_str());
}
}
4.用于展示内容的函数(调用文本)
void mode2_search_content()
{
FILE* fp = fopen("F:\\c语言\\ditto\\ditto\\数据文本(用户).txt", "r");
if (fp == NULL)
{
printf("文件打开失败");
}
//依次读取字符,防止内容缺失等问题
char ch;
while (EOF != (ch = fgetc(fp)))
{
printf("%c", ch);
}
fclose(fp);
}
5.用户保存功能与不保存功能
//保存功能实现函数
Ditto *fun2(Ditto* prev, const char result[65535])
{
// 创建新的Ditto对象并插入数据库中
int max_num = DittoManager::GetInstance()->get_max_ditto_num();//得到数据库中最大的序号
Ditto* current = new Ditto();//定义一个新Ditto指针类型变量
current->num = max_num + 1;
current->content = result;
current->date = get_time();
// 询问用户是否需要添加标题
char panduan;
printf("是否需要添加标题?是请输入 Y/y,否则输入其他任意键:");
消耗掉输入缓冲区中的换行符
//while (getchar() != '\n') {}
getchar();
panduan = getchar();
if (panduan == 'y' || panduan == 'Y')
{
printf("请输入标题:");
cin >> current->title;//利用cin获取标题,用scanf存在缓冲区问题,无法正常读取标题。
}
else
{
current->title="无自定义标题";
}
// 插入数据到数据库中
DittoManager::GetInstance()->insert_ditto(*current);
prev = current;
//将数据储存到文本中,方便后续的展示
FILE* fp = fopen("F:\\c语言\\ditto\\ditto\\数据文本(用户).txt", "a+");
if (fp == NULL)
{
printf("文件打开失败");
}
fprintf(fp, "序号: %d \n标题:%s \n复制内容: %s \n复制时间: %s\n\n", current->num,current->title.c_str(), current->content.c_str(), current->date.c_str());
fclose(fp);
//用于更新内容
return current;
}
//不保存功能实现,只需要不插入数据库中,然后更新内容即可
Ditto* fun2_(Ditto* prev, const char result[65535])
{
// 创建新的Ditto对象但并不需要插入到数据库中,仅为了更新prev中的内容
int max_num = DittoManager::GetInstance()->get_max_ditto_num();
Ditto* current = new Ditto();
current->num = max_num+1;
current->content = result;
current->date = get_time();
current->title = "无自定义标签";
// 打印新数据并更新prev指针
prev = current;
return current;
}
6.用户查找功能
i.游客查找–序号
过渡函数
void mode2_search_by_num(string key)
{
string keyword = key;
user_search_by_num(string(keyword));
}
实现函数
// 根据关键字搜索并打印Ditto记录的函数
void user_search_by_num(const string& keyword)
{
// 在内容或日期字段中搜索包含关键字的记录
vector<Ditto> results = DittoManager::GetInstance()->search_ditto_by_num(keyword);
if (results.empty()) {
printf("未找到包含关键字\"%s\"的记录。\n", keyword.c_str());
}
else {
printf("以下是包含关键字\"%s\"的记录:\n\n", keyword.c_str());
print_search_dittos(results);
}
}
相关数据库运行文件
vector<Ditto> DittoManager::search_ditto_by_num(string condition)
{
vector<Ditto> stuList;
char sql[1024];
int keyword_int = std::stoi(condition); // 将字符串转换为整数
sprintf(sql, "SELECT * FROM database_ditto WHERE num LIKE '%%%d%%'", keyword_int);
if (mysql_query(con, sql))
{
fprintf(stderr, "Failed to selete data , Error : %s\n", mysql_error(con));
return {};
}
MYSQL_RES* res = mysql_store_result(con);
MYSQL_ROW row;
while ((row = mysql_fetch_row(res)))
{
Ditto stu;
stu.num = atoi(row[0]);
stu.title = row[1];
stu.content = row[2];
stu.date = row[3];
stuList.push_back(stu);
}
return stuList;
}
ii.游客查找–标题
过渡函数
void mode2_search_by_title(string key)
{
string keyword = key;
//printf("key = %s",key.c_str()); //用于调试
user_search_by_title(string(keyword));
}
实现函数
void user_search_by_title(const string& keyword)
{
// 在内容或日期字段中搜索包含关键字的记录
vector<Ditto> results = DittoManager::GetInstance()->search_ditto_by_title(keyword);
if (results.empty()) {
printf("未找到包含关键字\"%s\"的记录。\n", keyword.c_str());
}
else {
printf("以下是包含关键字\"%s\"的记录:\n\n", keyword.c_str());
print_search_dittos(results);
}
}
相关数据库运行文件
vector<Ditto> DittoManager::search_ditto_by_title(string condition)
{
vector<Ditto> stuList;
char sql[1024];
sprintf(sql, "SELECT * FROM database_ditto WHERE title LIKE '%%%s%%' ", condition.c_str());
if (mysql_query(con, sql))
{
fprintf(stderr, "Failed to selete data , Error : %s\n", mysql_error(con));
return {};
}
MYSQL_RES* res = mysql_store_result(con);
MYSQL_ROW row;
while ((row = mysql_fetch_row(res)))
{
Ditto stu;
stu.num = atoi(row[0]);
stu.title = row[1];
stu.content = row[2];
stu.date = row[3];
stuList.push_back(stu);
}
return stuList;
}
iii.游客查找–内容
过渡函数
void mode2_search_by_content(string key)
{
string keyword = key;
user_search_by_content(string(keyword));
}
实现函数
void user_search_by_content(const string& keyword)
{
// 在内容或日期字段中搜索包含关键字的记录
vector<Ditto> results = DittoManager::GetInstance()->search_ditto_by_content(keyword);
if (results.empty()) {
printf("未找到包含关键字\"%s\"的记录。\n", keyword.c_str());
}
else {
printf("以下是包含关键字\"%s\"的记录:\n\n", keyword.c_str());
print_search_dittos(results);
}
}
相关数据库运行文件
vector<Ditto> DittoManager::search_ditto_by_content(string condition)
{
vector<Ditto> stuList;
char sql[1024];
sprintf(sql, "SELECT * FROM database_ditto WHERE content LIKE '%%%s%%' ", condition.c_str());
if (mysql_query(con, sql))
{
fprintf(stderr, "Failed to selete data , Error : %s\n", mysql_error(con));
return {};
}
MYSQL_RES* res = mysql_store_result(con);
MYSQL_ROW row;
while ((row = mysql_fetch_row(res)))
{
Ditto stu;
stu.num = atoi(row[0]);
stu.title = row[1];
stu.content = row[2];
stu.date = row[3];
stuList.push_back(stu);
}
return stuList;
}
IV.游客查找–时间
过渡函数
void mode2_search_by_date(string key)
{
string keyword = key;
user_search_by_date(string(keyword));
}
实现函数
void user_search_by_date(const string& keyword)
{
// 在内容或日期字段中搜索包含关键字的记录
vector<Ditto> results = DittoManager::GetInstance()->search_ditto_by_date(keyword);
if (results.empty()) {
printf("未找到包含关键字\"%s\"的记录。\n", keyword.c_str());
}
else {
printf("以下是包含关键字\"%s\"的记录:\n\n", keyword.c_str());
print_search_dittos(results);
}
}
相关数据库运行文件
vector<Ditto> DittoManager::search_ditto_by_date(string condition)
{
vector<Ditto> stuList;
char sql[1024];
sprintf(sql, "SELECT * FROM database_ditto WHERE date LIKE '%%%s%%' ", condition.c_str());
if (mysql_query(con, sql))
{
fprintf(stderr, "Failed to selete data , Error : %s\n", mysql_error(con));
return {};
}
MYSQL_RES* res = mysql_store_result(con);
MYSQL_ROW row;
while ((row = mysql_fetch_row(res)))
{
Ditto stu;
stu.num = atoi(row[0]);
stu.title = row[1];
stu.content = row[2];
stu.date = row[3];
stuList.push_back(stu);
}
return stuList;
}
(3)总代码
①主函数总代码
#include<stdio.h>
#include"DittoManager.h"
#include <time.h>
#include<string.h>
#include<windows.h>
typedef struct Ditto2
{
int num;
char title[128];
char content[65535];
char time[128];
Ditto2* next;
}Ditto2;
//定义选择模式1后,用户输入的身份即相关密码
const char* user = "游客";
const char* Administrator = "用户";
const char* PassWord = "123456";
//用于获取系统时间函数
char* get_time();
//进入模式1函数(以游客身份进入Cditto软件)
void mode1();
Ditto2 * fun1(Ditto2* prev,const char result[65535]);
Ditto2* fun1_(Ditto2* prev, const char result[65535]);
void youke_search(Ditto2* head);
void search_by_num(Ditto2* head, const char* key);
void search_by_title(Ditto2* head, const char* key);
//进入模式2函数(以用户身份进入Cditto软件)
//打印数据库不足十条时的结果
void print_dittos(const vector<Ditto>& dittos);
void print_search_dittos(const vector<Ditto>& dittos);
void print_dittos_all();
void user_search_by_num(const string& keyword);
void user_search_by_title(const string& keyword);
void user_search_by_content(const string& keyword);
void user_search_by_date(const string& keyword);
Ditto* fun2(Ditto* prev, const char result[65535]);
Ditto* fun2_(Ditto* prev, const char result[65535]);
void mode2();
void mode2_search_by_num(string key);
void mode2_search_by_title(string key);
void mode2_search_by_content(string key);
void mode2_search_by_date(string key);
//查看帮助文档
void help();
//用于监控剪切板内容的第三方函数
int exec_cmd_1(char* cmd, char* result);
//用于游客查看复制内容函数
void search_content();
//用于用户查看复制内容
void mode2_search_content();
int main()
{
char username[128];
char choose[128];
system("cls");
while (1)
{
system("cls");
printf("///欢迎使用Cditto\n");
printf("请输入相应序号选择功能\n");
printf("1.选择身份进入Cditto软件\n");
printf("2.查看帮助文档(游客和用户的区别)\n");
printf("3.退出Cditto程序\n");
printf("请输入序号进行选择: ");
scanf("%s", &choose);
if (strcmp(choose, "1") == 0)
{
while (1)
{
system("cls");
printf("请选择身份进入软件(游客/用户,用户需要输入密码,输入3返回目录)\n");
printf("请输入选择的身份: ");
scanf("%s",username);
getchar();
if (strcmp(username, user) == 0 || strcmp(username,"youke") == 0)
{
mode1();
}
else if (strcmp(username, Administrator) == 0 || strcmp(username, "yonghu") == 0)
{
mode2();
}
else if (strcmp(username, "3") == 0)
{
break;
}
else
{
printf("您输入的序号不正确,请重新输入......\n");
}
system("pause");
}
}
else if (strcmp(choose, "2") == 0)
{
help();
}
else if (strcmp(choose, "3") == 0)
{
FILE* fp1 = fopen("F:\\c语言\\ditto\\ditto\\数据文本.txt", "w+");
if (fp1 == NULL)
{
printf("文件打开失败");
}
fprintf(fp1, "%c",'\0');
fclose(fp1);
FILE* fp2 = fopen("F:\\c语言\\ditto\\ditto\\数据文本(用户).txt", "w+");
if (fp2 == NULL)
{
printf("文件打开失败");
}
fprintf(fp2, "%c", '\0');
fclose(fp2);
printf("欢迎下次使用");
return 0;
}
else
{
printf("您输入的序号不正确......\n");
}
system("pause");
}
return 0;
}
//利用time库获取时间,再利用localtime函数,将时间转化为系统时间
//原因time() 函数会读取操作系统的时钟,然后返回自 "1970 年 1 月 1 日 00:00:00" 到当前时间所经过的秒数。因此,time() 返回的值可以用于计算两个时间之间的时间差,或者将其转换为可读格式,如年、月、日、时、分和秒等。
char* get_time()
{
time_t rawtime;//用于获取时间
struct tm* timeinfo;
time(&rawtime);
//cout << rawtime;
timeinfo = localtime(&rawtime);//将获取到的时间转化为系统实际记录下来
//printf("Current local time and date: %s", asctime(timeinfo));
return asctime(timeinfo);
//获取到时间后的写法(调用函数后的)
//char* time = get_time();
//cout << time;
//printf("当前时间是: %s", time);
}
void mode1()
{
char panduan[128];
printf("欢迎使用cditto,您的身份是: 游客\n");
Ditto2* head = NULL;
Ditto2* prev = NULL; // 前驱指针初始化为NULL
Ditto2* temp = prev;
while (1)
{
system("pause");
system("cls");
printf("请选择功能:\n");
printf("1.记录模式\n");
printf("2.查找模式\n");
printf("3.退回上一级目录\n");
scanf("%s", panduan);
if (strcmp(panduan, "1") == 0)
{
system("cls");
while (1)
{
char result[65535] = { 0 };
char cmd[] = "powershell -command \"Get-Clipboard\"";
exec_cmd_1(cmd, result);
// 如果剪贴板内容发生改变
if (temp == NULL || strcmp(result, temp->content) != 0)
{
system("cls");
//判断是否需要记录
printf("您已复制新内容,是否保存\n");
printf("Y/y保存,Q/q返回上级目录,其余任意键取消保存\n");
// 消耗掉输入缓冲区中的换行符
while (getchar() != '\n') {}
char input = getchar();
if (input == 'Y' || input == 'y')
{
prev = fun1(prev, result);
// 如果prev为空,则说明链表为空,需要将head指向当前节点
if (head == NULL)
{
head = prev;
}
temp = prev;
}
else if (input == 'Q' || input == 'q')
{
break;
}
else
{
temp = fun1_(temp, result);
}
system("cls");
//输出结果
search_content();
Sleep(500); // 可以适当休眠减少CPU占用率
}
}
}
else if (strcmp(panduan, "2") == 0)
{
youke_search(head);
}
else if (strcmp(panduan, "3") == 0)
{
break;
}
else
{
printf("您输入的序号不对,请重新输入......");
}
}
}
Ditto2 *fun1(Ditto2* prev, const char result[65535])
{
FILE* fp = fopen("F:\\c语言\\ditto\\ditto\\数据文本.txt", "a+");
if (fp == NULL)
{
printf("文件打开失败");
return prev;
}
Ditto2* current = (Ditto2*)malloc(sizeof(Ditto2));
current->num = prev == NULL ? 1 : prev->num + 1;
strcpy(current->content, result);
strcpy(current->time, get_time());
current->next = NULL;
// 询问用户是否需要添加标题
char panduan;
printf("是否需要添加标题?是请输入 Y/y,否则输入其他任意键:");
// 消耗掉输入缓冲区中的换行符
while (getchar() != '\n') {}
panduan = getchar();
if (panduan == 'y' || panduan == 'Y')
{
printf("请输入标题:");
scanf("%s", current->title);
}
else
{
strcpy(current->title,"无自定义标题");
}
if (prev == NULL)
{
prev = current;
}
else
{
prev->next = current;
prev = current;
}
fprintf(fp, "序号:%d\n标题:%s\n复制内容:%s\n复制时间:%s\n\n",
current->num, current->title, current->content, current->time);
fclose(fp);
return current;
}
Ditto2* fun1_(Ditto2* temp, const char result[65535])
{
Ditto2* current = (Ditto2*)malloc(sizeof(Ditto2));
current->num = 0;
strcpy(current->content, result);
strcpy(current->time, get_time());
current->next = NULL;
// 消耗掉输入缓冲区中的换行符
while (getchar() != '\n') {}
strcpy(current->title, "无自定义标题");
return current;
}
void youke_search(Ditto2* head)
{
int choice;
char key[50];
printf("请选择搜索方式:\n");
printf("1. 按照序号搜索\n");
printf("2. 按照标题搜索\n");
scanf("%d", &choice);
switch (choice) {
case 1:
printf("请输入要搜索的序号:");
scanf("%s", key);
search_by_num(head, key);
break;
case 2:
printf("请输入要搜索的标题:");
scanf("%s", key);
search_by_title(head, key);
break;
default:
printf("无效的选项,请重新选择。\n");
break;
}
}
void search_by_num(Ditto2* head, const char* key)
{
Ditto2* p = head;
int num = atoi(key); // 将关键字转换成整数
while (p != NULL)
{
if (p->num == num)
{ // 如果序号匹配成功
printf("序号:%d\n标题:%s\n复制内容:%s\n复制时间:%s\n\n",
p->num, p->title, p->content, p->time);
return;
}
p = p->next;
}
printf("未找到匹配项。\n");
}
void search_by_title(Ditto2* head, const char* key)
{
Ditto2* p = head;
while (p != NULL) {
if (strcmp(p->title, key) == 0) { // 如果标题匹配成功
printf("序号:%d\n标题:%s\n复制内容:%s\n复制时间:%s\n\n",
p->num, p->title, p->content, p->time);
return;
}
p = p->next;
}
printf("未找到匹配项。\n");
}
//打印容器dittos中所有的(数据库)内容
void print_dittos(const vector<Ditto>& dittos)
{
for (int i = 0; i < dittos.size(); i++)
{
//printf("序号: %d \n标题:%s \n复制内容: %s \n复制时间: %s\n", dittos[i].num, dittos[i].title.c_str(), dittos[i].content.c_str(), dittos[i].date.c_str());
//printf("\n\n");
FILE* fp = fopen("F:\\c语言\\ditto\\ditto\\数据文本(用户).txt", "a+");
if (fp == NULL)
{
printf("文件打开失败");
}
fprintf(fp,"序号: %d \n标题:%s \n复制内容: %s \n复制时间: %s\n\n",dittos[i].num, dittos[i].title.c_str(), dittos[i].content.c_str(), dittos[i].date.c_str());
fclose(fp);
}
}
//打印搜索结果
void print_search_dittos(const vector<Ditto>& dittos)
{
for (int i = 0; i < dittos.size(); i++)
{
//printf("序号: %d \n标题:%s \n复制内容: %s \n复制时间: %s\n", dittos[i].num, dittos[i].title.c_str(), dittos[i].content.c_str(), dittos[i].date.c_str());
//printf("\n\n");
printf("序号: %d \n标题:%s \n复制内容: %s \n复制时间: %s\n\n", dittos[i].num, dittos[i].title.c_str(), dittos[i].content.c_str(), dittos[i].date.c_str());
}
}
// 打印所有Ditto记录的函数
void print_dittos_all()
{
vector<Ditto> ret = DittoManager::GetInstance()->get_ditto();
for (auto& t : ret)
{
cout << "ID: " << t.num << '\n' << "title: " <<t.title << '\n' << "Content: " << t.content << '\n' << "date: " << t.date << endl;
}
}
// 根据关键字搜索并打印Ditto记录的函数
void user_search_by_num(const string& keyword)
{
// 在内容或日期字段中搜索包含关键字的记录
vector<Ditto> results = DittoManager::GetInstance()->search_ditto_by_num(keyword);
if (results.empty()) {
printf("未找到包含关键字\"%s\"的记录。\n", keyword.c_str());
}
else {
printf("以下是包含关键字\"%s\"的记录:\n\n", keyword.c_str());
print_search_dittos(results);
}
}
void user_search_by_title(const string& keyword)
{
// 在内容或日期字段中搜索包含关键字的记录
vector<Ditto> results = DittoManager::GetInstance()->search_ditto_by_title(keyword);
if (results.empty()) {
printf("未找到包含关键字\"%s\"的记录。\n", keyword.c_str());
}
else {
printf("以下是包含关键字\"%s\"的记录:\n\n", keyword.c_str());
print_search_dittos(results);
}
}
void user_search_by_content(const string& keyword)
{
// 在内容或日期字段中搜索包含关键字的记录
vector<Ditto> results = DittoManager::GetInstance()->search_ditto_by_content(keyword);
if (results.empty()) {
printf("未找到包含关键字\"%s\"的记录。\n", keyword.c_str());
}
else {
printf("以下是包含关键字\"%s\"的记录:\n\n", keyword.c_str());
print_search_dittos(results);
}
}
void user_search_by_date(const string& keyword)
{
// 在内容或日期字段中搜索包含关键字的记录
vector<Ditto> results = DittoManager::GetInstance()->search_ditto_by_date(keyword);
if (results.empty()) {
printf("未找到包含关键字\"%s\"的记录。\n", keyword.c_str());
}
else {
printf("以下是包含关键字\"%s\"的记录:\n\n", keyword.c_str());
print_search_dittos(results);
}
}
// 根据序号删除Ditto记录的函数
void delete_ditto(int num)
{
bool success = DittoManager::GetInstance()->delete_ditto(num);
if (success) {
printf("已成功删除序号为%d的记录。\n", num);
}
else {
printf("未找到序号为%d的记录。\n", num);
}
}
void mode2()
{
char password[128];
while (1)
{
system("cls");
printf("欢迎使用cditto,您的身份是: 用户\n");
printf("请输入密码(输入back返回上一界面):");
scanf("%s", password);
if (strcmp(password, PassWord) == 0)
{
while (1)
{
system("cls");
// 打印菜单选项
printf("1.记录模式 \n2.查找模式 \n3.显示所有记录 \n4.删除模式 \n5.返回上级目录\n\n");
int choice;
printf("请选择功能:");
scanf("%d", &choice);
while (getchar() != '\n') {}
if (choice == 1) // 记录模式
{
system("cls");
// 获取数据库中的所有记录
vector<Ditto> ret = DittoManager::GetInstance()->get_ditto();
// 如果记录数量不足10行,则打印全部记录
if (ret.size() <= 10)
{
print_dittos(ret);
}
else
{
// 获取最后10行数据并打印
int start = ret.size() - 10;
vector<Ditto> last_10;
for (int i = start; i < ret.size(); i++)
{
last_10.push_back(ret[i]);
}
print_dittos(last_10);
}
Ditto* prev = nullptr;
while (true)
{
char result[65535] = { 0 };
char cmd[] = "powershell -command \"Get-Clipboard\"";
exec_cmd_1(cmd, result);
// 如果剪贴板内容发生改变
if (prev == nullptr || strcmp(result, prev->content.c_str()) != 0)
{
system("cls");
//判断是否需要记录
printf("您已复制新内容,是否保存\n");
printf("Y/y保存,Q/q返回上级目录,其余任意键取消保存\n");
// 消耗掉输入缓冲区中的换行符
while (getchar() != '\n') {}
char input = getchar();
if (input == 'Y' || input == 'y')
{
prev = fun2(prev, result);
}
else if (input == 'Q' || input == 'q')
{
break;
}
else
{
prev = fun2_(prev, result);
}
//输出结果
system("cls");
mode2_search_content();
}
Sleep(500);
}
}
else if (choice == 2) // 查找模式
{
system("cls");
int select;
string key;
printf("请选择搜索方式(均为模糊搜索):\n");
printf("1. 按照序号搜索\n");
printf("2. 按照标题搜索\n");
printf("3. 按照内容搜索\n");
printf("4. 按照日期搜索\n");
printf("请选择搜索方式:");
scanf("%d", &select);
switch (select) {
case 1:
printf("请输入要搜索的序号:");
cin >> key;
mode2_search_by_num(key);
break;
case 2:
printf("请输入要搜索的标题:");
cin >> key;
mode2_search_by_title(key);
break;
case 3:
printf("请输入要搜索的内容:");
cin >> key;
mode2_search_by_content(key);
break;
case 4:
printf("请输入要搜索的日期:");
cin >> key;
mode2_search_by_date(key);
break;
default:
printf("无效的选项,请重新选择。\n");
break;
}
//string keyword;
//printf("请输入要搜索的关键字:");
//scanf("%s", keyword);
//search_dittos(string(keyword));
}
else if (choice == 3) // 显示所有记录
{
system("cls");
print_dittos_all();
}
else if (choice == 4) // 删除模式
{
system("cls");
int num;
printf("请输入要删除的记录序号:");
scanf("%d", &num);
delete_ditto(num);
}
else if (choice == 5) // 返回上级目录
{
return;
}
else
{
printf("无效选择,请重新输入。\n");
}
system("pause");
}
}
else if (strcmp(password, "back") == 0)
{
break;
}
else
{
printf("您输入的密码不对,请重新输入......\n");
}
system("pause");
}
}
Ditto *fun2(Ditto* prev, const char result[65535])
{
// 创建新的Ditto对象并插入数据库中
int max_num = DittoManager::GetInstance()->get_max_ditto_num();
Ditto* current = new Ditto();
current->num = max_num + 1;
current->content = result;
current->date = get_time();
// 询问用户是否需要添加标题
char panduan;
printf("是否需要添加标题?是请输入 Y/y,否则输入其他任意键:");
消耗掉输入缓冲区中的换行符
//while (getchar() != '\n') {}
getchar();
panduan = getchar();
if (panduan == 'y' || panduan == 'Y')
{
printf("请输入标题:");
cin >> current->title;
}
else
{
current->title="无自定义标题";
}
// 插入数据到数据库中
DittoManager::GetInstance()->insert_ditto(*current);
prev = current;
FILE* fp = fopen("F:\\c语言\\ditto\\ditto\\数据文本(用户).txt", "a+");
if (fp == NULL)
{
printf("文件打开失败");
}
fprintf(fp, "序号: %d \n标题:%s \n复制内容: %s \n复制时间: %s\n\n", current->num,current->title.c_str(), current->content.c_str(), current->date.c_str());
fclose(fp);
return current;
}
Ditto* fun2_(Ditto* prev, const char result[65535])
{
// 创建新的Ditto对象但并不需要插入到数据库中,仅为了更新prev中的内容
int max_num = DittoManager::GetInstance()->get_max_ditto_num();
Ditto* current = new Ditto();
current->num = max_num+1;
current->content = result;
current->date = get_time();
current->title = "无自定义标签";
// 打印新数据并更新prev指针
prev = current;
return current;
}
void mode2_search_by_num(string key)
{
string keyword = key;
user_search_by_num(string(keyword));
}
void mode2_search_by_title(string key)
{
string keyword = key;
//printf("key = %s",key.c_str());
user_search_by_title(string(keyword));
}
void mode2_search_by_content(string key)
{
string keyword = key;
user_search_by_content(string(keyword));
}
void mode2_search_by_date(string key)
{
string keyword = key;
user_search_by_date(string(keyword));
}
void help()
{
system("cls");
//打开文件
FILE* fp = fopen("F:\\c语言\\ditto\\ditto\\帮助文档(1).txt","r");
if (fp == NULL)
{
printf("文件打开失败");
}
//依次读取字符,防止内容缺失等问题
char ch;
while (EOF != (ch = fgetc(fp)))
{
printf("%c", ch);
}
fclose(fp);
putchar('\n');
}
void search_content()
{
FILE* fp = fopen("F:\\c语言\\ditto\\ditto\\数据文本.txt", "r");
if (fp == NULL)
{
printf("文件打开失败");
}
//依次读取字符,防止内容缺失等问题
char ch;
while (EOF != (ch = fgetc(fp)))
{
printf("%c", ch);
}
fclose(fp);
}
void mode2_search_content()
{
FILE* fp = fopen("F:\\c语言\\ditto\\ditto\\数据文本(用户).txt", "r");
if (fp == NULL)
{
printf("文件打开失败");
}
//依次读取字符,防止内容缺失等问题
char ch;
while (EOF != (ch = fgetc(fp)))
{
printf("%c", ch);
}
fclose(fp);
}
/****************************************************第三方函数的调用********************************************************************************/
//监控剪切板的第三方函数
int exec_cmd_1(char* cmd, char* result)
{
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
HANDLE h_read, h_write;
if (!CreatePipe(&h_read, &h_write, &sa, 0)) {
return 0;
}
STARTUPINFO si = { sizeof(STARTUPINFO) };
GetStartupInfo(&si);
si.wShowWindow = SW_HIDE;
si.hStdError = NULL;
si.hStdOutput = h_write;
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
PROCESS_INFORMATION pi;
if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi)) {
return -1;
}
CloseHandle(h_write);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
char buffer[1024];
DWORD bytes_read;
while (ReadFile(h_read, buffer, 1023, &bytes_read, NULL)) {
if (bytes_read == 0) {
break;
}
buffer[bytes_read] = '\0'; // 确保读取的字符串以'\0'结尾
strcat(result, buffer);
memset(buffer, 0, sizeof(buffer)); // 清空缓冲区
}
CloseHandle(h_read);
return 1;
}
/**
* @brief 通过创建进程的方式无控制台窗口执行cmd
*
* @param[in] cmd 命令
* @return 1|0|-1 成功|管道创建失败|进程创建失败
*/
int exec_cmd_2(char* cmd)
{
STARTUPINFO si = { sizeof(STARTUPINFO) }; // 此结构体用于指定新进程的主窗口特性 //si.cb = sizeof(STARTUPINFO);
GetStartupInfo(&si);
si.wShowWindow = SW_HIDE;
si.dwFlags = STARTF_USESHOWWINDOW;
PROCESS_INFORMATION pi; // 此结构包含有关新进程及其主线程的信息
if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi))
return -1;
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return 1;
}
/************************************************************************************************************************************/
//实现对接数据库的函数
//int main()
//{
// //Ditto stu1{ 1, "这是复制内容1", "2023-4-27" };
// Ditto stu2{ 2, "这是复制内容2", "2023-4-27" };
//
//
// DittoManager::GetInstance()->update_ditto(stu1); // 更新,根据主变量
// DittoManager::GetInstance()->insert_ditto(stu2); // 插入
// //DittoManager::GetInstance()->delete_ditto(333333); // 删除,根据主变量
//
//
//vector<Ditto> ret = DittoManager::GetInstance()->get_ditto();
//for (auto& t : ret)
//{
// cout << "ID: " << t.num << ' ' << "Content: " << t.content << ' ' << "date: " << t.date << endl;
//}
//
//
// return 0;
//}
//
//
②数据库头文件总代码
#pragma once
#include<mysql.h>
#include<iostream>
#include<string>
#include<vector>
using namespace std;
typedef struct Ditto
{
int num;
string title;
string content;
string date;
}Ditto;
class DittoManager
{
DittoManager();
~DittoManager();
public:
static DittoManager* GetInstance() //单例模式
{
static DittoManager DittoManager;
return &DittoManager;
}
public:
bool insert_ditto(Ditto& t);
bool update_ditto(Ditto& t);
bool delete_ditto(int num);
vector<Ditto> get_ditto(string condition = "");
vector<Ditto> search_ditto_by_num(string condition = "");
vector<Ditto> search_ditto_by_title(string condition = "");
vector<Ditto> search_ditto_by_content(string condition = "");
vector<Ditto> search_ditto_by_date(string condition = "");
int get_max_ditto_num();
private:
MYSQL* con;
const char* host = "localhost";
const char* user = "root";
const char* pw = "jx20031002";
const char* database_name = "ditto";//数据库的名字 不是表的名字
const int port = 3306;
};
③数据库运行文件总代码
#include "DittoManager.h"
DittoManager::DittoManager()
{
con = mysql_init(NULL);
//设置字符编码
mysql_options(con, MYSQL_SET_CHARSET_NAME, "GBK");
if (!mysql_real_connect(con, host, user, pw, database_name, port, NULL, 0))
{
exit(1);
}
}
DittoManager::~DittoManager()
{
mysql_close(con);
}
bool DittoManager::insert_ditto(Ditto& stu)
{
char sql[1024];
sprintf(sql, "Insert ignore into database_ditto (num,title,content,date) values(%d, '%s', '%s', '%s')",
stu.num, stu.title.c_str(), stu.content.c_str(), stu.date.c_str());
if (mysql_query(con, sql))
{
fprintf(stderr, "Failed to Insert data , Error : %s\n", mysql_error(con));
return false;
}
return true;
}
bool DittoManager::update_ditto(Ditto& stu)
{
char sql[1024];
sprintf(sql, "UPDATE database_ditto SET title = '%s', content='%s', date='%s'"
"where num = %d",
stu.title.c_str(), stu.content.c_str(), stu.date.c_str(), stu.num);
if (mysql_query(con, sql))
{
fprintf(stderr, "Failed to update data , Error : %s\n", mysql_error(con));
return false;
}
return true;
}
bool DittoManager::delete_ditto(int num)
{
char sql[1024];
sprintf(sql, "DELETE FROM database_ditto WHERE num=%d ",
num);
if (mysql_query(con, sql))
{
fprintf(stderr, "Failed to delete data , Error : %s\n", mysql_error(con));
return false;
}
// 更新数据库剩下数据的num值(依次更新 -1 )
sprintf(sql, "UPDATE database_ditto SET num=num-1 WHERE num>%d", num);
if (mysql_query(con, sql))
{
fprintf(stderr, "Failed to update data , Error : %s\n", mysql_error(con));
return false;
}
printf("记录删除成功!\n");
return true;
}
vector<Ditto> DittoManager::get_ditto(string condition)
{
vector<Ditto> stuList;
char sql[1024];
sprintf(sql, "SELECT * FROM database_ditto %s ", condition.c_str());
if (mysql_query(con, sql))
{
fprintf(stderr, "Failed to selete data , Error : %s\n", mysql_error(con));
return {};
}
MYSQL_RES* res = mysql_store_result(con);
MYSQL_ROW row;
while ((row = mysql_fetch_row(res)))
{
Ditto stu;
stu.num = atoi(row[0]);
stu.title = row[1];
stu.content = row[2];
stu.date = row[3];
stuList.push_back(stu);
}
return stuList;
}
vector<Ditto> DittoManager::search_ditto_by_num(string condition)
{
vector<Ditto> stuList;
char sql[1024];
int keyword_int = std::stoi(condition); // 将字符串转换为整数
sprintf(sql, "SELECT * FROM database_ditto WHERE num LIKE '%%%d%%'", keyword_int);
if (mysql_query(con, sql))
{
fprintf(stderr, "Failed to selete data , Error : %s\n", mysql_error(con));
return {};
}
MYSQL_RES* res = mysql_store_result(con);
MYSQL_ROW row;
while ((row = mysql_fetch_row(res)))
{
Ditto stu;
stu.num = atoi(row[0]);
stu.title = row[1];
stu.content = row[2];
stu.date = row[3];
stuList.push_back(stu);
}
return stuList;
}
vector<Ditto> DittoManager::search_ditto_by_title(string condition)
{
vector<Ditto> stuList;
char sql[1024];
sprintf(sql, "SELECT * FROM database_ditto WHERE title LIKE '%%%s%%' ", condition.c_str());
if (mysql_query(con, sql))
{
fprintf(stderr, "Failed to selete data , Error : %s\n", mysql_error(con));
return {};
}
MYSQL_RES* res = mysql_store_result(con);
MYSQL_ROW row;
while ((row = mysql_fetch_row(res)))
{
Ditto stu;
stu.num = atoi(row[0]);
stu.title = row[1];
stu.content = row[2];
stu.date = row[3];
stuList.push_back(stu);
}
return stuList;
}
vector<Ditto> DittoManager::search_ditto_by_content(string condition)
{
vector<Ditto> stuList;
char sql[1024];
sprintf(sql, "SELECT * FROM database_ditto WHERE content LIKE '%%%s%%' ", condition.c_str());
if (mysql_query(con, sql))
{
fprintf(stderr, "Failed to selete data , Error : %s\n", mysql_error(con));
return {};
}
MYSQL_RES* res = mysql_store_result(con);
MYSQL_ROW row;
while ((row = mysql_fetch_row(res)))
{
Ditto stu;
stu.num = atoi(row[0]);
stu.title = row[1];
stu.content = row[2];
stu.date = row[3];
stuList.push_back(stu);
}
return stuList;
}
vector<Ditto> DittoManager::search_ditto_by_date(string condition)
{
vector<Ditto> stuList;
char sql[1024];
sprintf(sql, "SELECT * FROM database_ditto WHERE date LIKE '%%%s%%' ", condition.c_str());
if (mysql_query(con, sql))
{
fprintf(stderr, "Failed to selete data , Error : %s\n", mysql_error(con));
return {};
}
MYSQL_RES* res = mysql_store_result(con);
MYSQL_ROW row;
while ((row = mysql_fetch_row(res)))
{
Ditto stu;
stu.num = atoi(row[0]);
stu.title = row[1];
stu.content = row[2];
stu.date = row[3];
stuList.push_back(stu);
}
return stuList;
}
int DittoManager::get_max_ditto_num()
{
const char* query = "SELECT MAX(num) FROM database_ditto";
int max_num = -1;
if (mysql_query(con, query) == 0)
{
MYSQL_RES* res = mysql_store_result(con);
MYSQL_ROW row = mysql_fetch_row(res);
if (row != nullptr && row[0] != nullptr)
{
max_num = atoi(row[0]);
}
mysql_free_result(res);
}
else
{
printf("get_max_ditto_num failed: %s\n", mysql_error(con));
}
return max_num;
}
改善后效果展示
cditto演示视频