前几篇已经写出了很多的功能实现,但不够完善。本篇要展示的也并非是全部。这期间我改了一些小细节,主要是为了输入后的提醒能够及时,功能实现更加贴近现实,以及其他目的我也就忘记了。前几天写了linux和c++笔记,懒了几天后(过年过年,有那个氛围,允许一下)0今天又拿起通讯录写了起来,把之前的批量删除给补上了,经过简单测试,删除功能都正常。不过严谨肯定要有,只是我不算太清楚真正的测试方法,我只能给到更多人去使用来验证这个程序的实用性。接下来我慢慢说明批量删除的具体实现。
我的码云在这里https://gitee.com/kongqizyd/start-some-c-codes-for-learning.c/tree/master/Address%20Book。本篇也会展示已经写出来的代码。
#include "Contact.h"
void menu()
{
printf("\n");
printf("*************************************\n");
printf("******* 1. 增加 2. 删除 *******\n");
printf("******* 3. 查找 4. 修改 *******\n");
printf("******* 5. 展示 6. 排序 *******\n");
printf("******* 7. 分类 8. 保存 *******\n");
printf("******* 0. 退出 *******\n");
printf("*************************************\n");
printf("\n");
}
enum Options
{
Exit,
Add,
Del,
Find,
Modify,
Show,
Sort,
//Classify,
Save,
};
int main()
{
int input = 0;
Contact book;
InitContact(&book);
LoadContact(&book);
do
{
menu();
int n = 1;
while (n)
{
printf("请选择: >");
n = scanf("%d", &input);
if (n != 1 || input < 0 || input > 8 || n == 0)
{
printf("没有对应选项,请重新选择\n");
continue;
}
else
break;
}
switch (input)
{
case Add:
AddContact(&book);
break;
case Del:
DelContact(&book);
break;
case Find:
FindContact(&book);
break;
case Modify:
ModifyContact(&book);
break;
case Show:
ShowContact(&book);
break;
case Sort:
SortContact(&book);
break;
/*case Classify:
ClassifyContact(&book);
break;*/
case Save:
SaveContact(&book);
break;
case Exit:
SaveContact(&book);
DestroyContact(&book);
printf("退出通讯录\n");
break;
}
} while (input);
return 0;
}
这里的改动就在于选择哪个功能,以防止用户输错,所以错误就提醒一次,并重新输入。不过这里有个问题需要改动,如果用户输入了汉字等字符,那么程序就在显示完
没有对应选项,请重新选择一句话后退出通讯录,或者出现其它问题,其原因应当在于输入的格式是%d,所以字符自然会出错。至于这个如何解决,我还没有头绪,年后再去寻找办法。
后面的代码中一样会有这样的问题,用户输入的不是数字,程序出错。
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
#include
#define MAX 100
#define NAME 20//姓名
#define GENDER 7//性别
#define ADDRESS 20//地址
#define WORK 10//职业
#define NUMBER 15//电话
#define SIZE 30//初始化时动态开辟的空间
#define ADD_SZ 10//增容时所用
typedef struct PeoInfo
{
char name[NAME];
char gender[GENDER];
char address[ADDRESS];
char work[WORK];
char number[NUMBER];
int age;
}PeoInfo;
typedef struct Contact
{
PeoInfo* data;
int sz;
int capacity;
}Contact;
void LoadContact(Contact* ps);
void InitContact(Contact* ps);
void AddContact(Contact* ps);
void DelContact(Contact* ps);
int SpecifyDel(Contact* ps);
int BatchDel(Contact* pb);
void FindContact(Contact* ps);
void SortContact(Contact* ps);
void ModifyContact(const Contact* ps);
void ShowContact(const Contact* ps);
//void ClassifyContact(const Contact* ps);
void DestroyContact(Contact* ps);
void SaveContact(Contact* ps);
两个文件都可以看到,我有一个分类函数没写,这也是之后需要完善的一部分。
接下来才是重点——批量删除
这里我会只展示删除部分的代码,其它都有小细节的改动,不算大改。
先看删除的主体
void DelContact(Contact* pd)
{
assert(pd);
int n = 0;
int s = 1;
int b = 1;
int i = 0;
int num = 0;
char ret[20] = { '\0' };
printf("进行删除前请仔细看好每个联系人的代号\n");
ShowContact(pd);
printf("请选择删除模式: 1、指定删除 2、批量删除\n");
while (scanf("%d", &n) != EOF)
{
if (n == 1)
{
while (s)
{
s = SpecifyDel(pd);
if (s != 0)
++num;
if (3 == num)
{
printf("程序自身已出现错误3次,强制退出,请重新开始\n");
s = 0;
num = 0;
}
}
break;
}
else if (n == 2)
{
while (b)
{
b = BatchDel(pd);
if (b != 0)
++num;
if (3 == num)
{
printf("程序自身已出现错误3次,强制退出,请重新开始\n");
b = 0;
num = 0;
}
}
break;
}
else
{
printf("请重新输入\n");
continue;
}
}
}
删除分为指定删除和批量删除。在两个删除里,都需要开文件指针,往回收站文本文件里写入数据;开指针的时候要检查是否为NULL;函数其他地方都不会出这样的错误,只是代码写得怎么样,所以我把两个函数都改成int类型,出错就返回1.实际上我们都知道,创建指针出错这种问题的概率极小,可以忽略掉,不过只是出于严谨吧。指定删除(SpecifyDel)用s接受返回值,批量删除(BatchDel)用b接收返回值。
在之前的代码中,选择删除后,会询问用户是否查看已存在的联系人,现在已经改过,直接显示一遍,无论是指定还是批量都需要用到前面的序列号,所以不如直接显示一遍更好。
int BatchDel(Contact* pb)
{
assert(pb);
int num[30] = { 0 };
int n = 0;
int m = 1;
int size = 0;
printf("请输入要删除的序列号: 选择好后,输入 666 即可进行批量删除\n");
while (m && scanf("%d", &n) != EOF)
{
if (n == 666)
break;
num[size] = n;
if (size < 30)
size++;
if (size == 30)
{
printf("最多选择30个,是否开始删除? 0、是 1、否\n");
scanf("%d", &m);
if (m)
{
printf("您可以继续选择,但只会删除前30个\n");
}
}
}
InsertSort(num, size);
FILE* bin = fopen("E://recycle bin.txt", "a");
if (bin == NULL)
{
perror("ZDDel::fopen");
printf("文件创建失败,请重新进行删除\n");
return 1;
}
int x = pb->sz, y = 0, z = 0, ret = 0;
while (x > 1)
{
while (x != num[z])
{
x--;
}
y = x - 1;
while ((y + 1) == num[z] && z < size)
{
fprintf(bin, "姓名:%s 性别:%s 地址:%s 工作:%s 电话:%s 年龄:%d\n", pb->data[y].name, pb->data[y].gender, pb->data[y].address, pb->data[y].work, pb->data[y].number, pb->data[y].age);
y--;
z++;
}
y += 1;
ret = x - y;
for (int i = y; i < pb->sz - ret; i++)
{
pb->data[i] = pb->data[i + ret];
}
pb->sz = pb->sz - ret;
x = y;
}
fclose(bin);
bin = NULL;
return 0;
}
我们如何看待这些代码?
现实中我们可以看到的是,选择批量删除后,我们要选择删除哪几个,选中后点击删除即可。放到数组做的通讯录来说,就是选中的数字转换成可对应的数组的元素,然后全部删除掉,再也访问不到它们。
问题是,正如上方代码一样,用户选中删除哪几个后,程序如何开始删除?这里我没有想到解决办法,本来是想输入数字后让用户输入“删除”二字,但不知如何让数字和字符共存,我想到的就是检测到字符后就转换成数字,但还没有实现。所以我先用666来结束循环。
我设定好最多选择30个,也就写了个循环来控制数量。结束后就是批量删除的开始。我的代码思路使用了双指针思路,只不过这里是双变量。双变量之前,先把选好的序号排列一下,否则无论后面如何操作,无序的序号组都是一个麻烦。
起初我排了升序,x代表序号,从1开始走,遇到选中的序号就停下来,用y来继续遍历,把遇到的序号放入回收站文件里,碰到非选中的序号就结束循环,然后后面的元素往前覆盖,控制好步数即可。但是走错了,很麻烦。在我开始写覆盖的代码时就发现了一个问题,如果选中的是3和5呢?y经历过3的位置后,会停在4的位置,这时候开始覆盖,4会来到x停下的位置,5会在其后,那么y是不是又得再来一次?从前往后走不好控制,容易控制不住,出现越界问题,删错数据。所以我改为从后往前,x初始值设为通讯录sz的大小,也就是联系人数量的大小。从后往前走,遇到一个选中的序列号后,循环还是交给y,让y继续往前走,遇到非选中的序号就停下,然后后覆盖前,这里就不需要担心是否还有临近的序号要删除。覆盖完后,x也得来到y的附件,继续往前删除。不过这里就出现了一个问题,要想真正做到不担心附近是否有选中序号,序号组就需要降序,让x先遇到大的序号,这样小的序号都在左边,就不会出现问题。另一个要解决的就是边界问题,或者说这也是删除数据常出现的问题之一。
int x = pb->sz, y = 0, z = 0, ret = 0;
while (x > 1)
{
while (x != num[z])
{
x--;
}
y = x - 1;
while ((y + 1) == num[z] && z < size)
{
fprintf(bin, "姓名:%s 性别:%s 地址:%s 工作:%s 电话:%s 年龄:%d\n", pb->data[y].name, pb->data[y].gender, pb->data[y].address, pb->data[y].work, pb->data[y].number, pb->data[y].age);
y--;
z++;
}
y += 1;
ret = x - y;
for (int i = y; i < pb->sz - ret; i++)
{
pb->data[i] = pb->data[i + ret];
}
pb->sz = pb->sz - ret;
x = y;
}
这一部分就是我经过不断调试后才成功的代码。这里有一个考虑,假如选择1 4 6,那么x遇到6后停下,此时y = 5,进入循环后,y–, = 4,这时候循环会退出,然后开始往前覆盖。这也就说明一个现象,序号为5时,也就是下标为4的元素不是选中的序号,所以x应当从4开始继续往前走,所以最后有一行x = y。
代码运行到这里,也要注意指针的回收。
这就是做好的批量删除的思路。实际通讯录中批量删除如何实现我并不清楚,这仅仅是我自己能够想到的思路。之前做过以空间换时间,再创建一个结构体,删除时用它来存储删除后的联系人,然后再拷贝回去,这条思路也可,不过在数组上,我认为双变量更简单。
关于还没写到的代码,有输入非数字的解决,分类函数,程序开始删除等问题,年后会继续改善。现在写出来的代码算是整体的框架已经完成,剩下的是修改以及更有效的代码实现。年后继续更新,可去我的码云看完整的代码。
给各位看官拜年了!
结束。