华科信安18级c语言课设

目录

要求

本次课设有两道题目,笔者抽中的是菜农管理系统的设计。课设要求使用十字链表作为数据结构。

数据结构

首先定义十字链表的结构,由于是三叉十字链表,本次定义了三个节点

int	plantGid = 0;
struct PrimaryNode
{
	char classId;
	char className[8];
	PrimaryNode* nextVegClass;
	SecondaryList* vegInfos;
	PrimaryNode* prevVegClass;
};

struct SecondaryNode
{
	int vegId;
	char vegName[20];
	char nutrition[50];
	SecondaryNode* nextVegInfo;
	TertiaryList* vegs;
	SecondaryNode* prevVegInfo;
};
struct TertiaryNode
{
	int id;
	int area;
	float weight;
	char year[5];
	TertiaryNode* nextVeg;
	TertiaryNode* prevVeg;
};

同时,定义了三种list结构,管理节点

struct TertiaryList
{
	TertiaryNode* head;
	int count = 0;
	void init() {
		head = MallocTertiaryNode();
		count = 0;
	}
	TertiaryNode* Where(std::function<bool(TertiaryNode*)> lambda) {
		TertiaryNode* current = head;
		while (true)
		{
			current = current->nextVeg;
			if (current == nullptr)
			{
				return nullptr;
			}
			else if (lambda(current))
			{
				return current;
			}
		}
		return current;
	}
	TertiaryNode* While(std::function<bool(TertiaryNode*)> lambda) {
		TertiaryNode* current2 = head;
		while (true)
		{
			current2 = current2->nextVeg;
			if (current2 == nullptr)
			{
				break;
			}
			if (!lambda(current2))
			{
				if (current2->nextVeg==nullptr)
				{
					current2->prevVeg->nextVeg = nullptr;
					break;
				}
				current2->prevVeg->nextVeg = current2->nextVeg;
				current2->nextVeg->prevVeg = current2->prevVeg;
			}
		}
		return head->nextVeg;
	}
	void Remove(int pos) {
		count--;
		if (pos == 0)
		{
			TertiaryNode* del = head->nextVeg;
			head->nextVeg = head->nextVeg->nextVeg;
			head->nextVeg->prevVeg = head;
			free(del);
			return;
		}

		TertiaryNode* prev = ElementAt(pos - 1);
		TertiaryNode* del = prev->nextVeg;
		prev->nextVeg = prev->nextVeg->nextVeg;
		prev->nextVeg->prevVeg = prev;
		free(del);
	}
	void Add(int area, float weight, const char* year) {
		TertiaryNode* current = this->head;
		while (true)
		{
			if (current->nextVeg == nullptr)
			{
				current->nextVeg = MallocTertiaryNode(plantGid+1, area, weight, year);
				current->nextVeg->nextVeg = nullptr;
				current->nextVeg->prevVeg = current;
				plantGid++;
				break;
			}
			current = current->nextVeg;
		}
		count++;
	}
	void Add(int id, int area, float weight, const char* year) {
		TertiaryNode* current = this->head;
		while (true)
		{
			if (current->nextVeg == nullptr)
			{
				current->nextVeg = MallocTertiaryNode(id, area, weight, year);
				current->nextVeg->nextVeg = nullptr;
				current->nextVeg->prevVeg = current;
				plantGid++;
				break;
			}
			current = current->nextVeg;
		}
	}

	TertiaryNode* ElementAt(int pos) {
		TertiaryNode* current = head;
		while (true)
		{
			current = current->nextVeg;
			if (pos == 0)
			{
				break;
			}
			pos--;
		}
		return current;
	}



	TertiaryNode* MallocTertiaryNode(int id, int area, float weight, const char* year) {
		TertiaryNode* node = (TertiaryNode*)malloc(sizeof(TertiaryNode));
		node->area = area;
		node->id = id;
		node->weight = weight;
		node->nextVeg = nullptr;
		int len = strlen(year);
		for (size_t i = 0; i < len + 1; i++)
		{
			node->year[i] = year[i];
		}
		return node;
	}
	TertiaryNode* MallocTertiaryNode() {
		TertiaryNode* node = (TertiaryNode*)malloc(sizeof(TertiaryNode)*2);
		node->nextVeg = nullptr;
		return node;
	}
};

struct SecondaryList
{
	SecondaryNode* head;
	int count = 0;
	void init() {
		head = MallocSecondaryNode();
		count = 0;
	}

	void Add(const char* vegName, const char* nutrtion) {
		SecondaryNode* current = this->head;
		while (true)
		{
			if (current->nextVegInfo == nullptr)
			{
				current->nextVegInfo = MallocSecondaryNode(vegGid+1, vegName, nutrtion);
				current->nextVegInfo->vegs = (TertiaryList*)malloc(sizeof(TertiaryList) * 2);
				current->nextVegInfo->vegs->init();
				current->nextVegInfo->nextVegInfo = nullptr;
				current->nextVegInfo->prevVegInfo = current;
				vegGid++;
				break;
			}
			current = current->nextVegInfo;
		}
		count++;
	}

	void Add(int gid,const char* vegName, const char* nutrtion) {
		SecondaryNode* current = this->head;
		while (true)
		{
			if (current->nextVegInfo == nullptr)
			{
				current->nextVegInfo = MallocSecondaryNode(gid, vegName, nutrtion);
				current->nextVegInfo->vegs = (TertiaryList*)malloc(sizeof(TertiaryList) * 2);
				current->nextVegInfo->vegs->init();
				current->nextVegInfo->nextVegInfo = nullptr;
				current->nextVegInfo->prevVegInfo = current;
				vegGid++;
				break;
			}
			current = current->nextVegInfo;
		}
		count++;
	}
	SecondaryNode* Where(std::function<bool(SecondaryNode*)> lambda) {
		SecondaryNode* current = head;
		while (true)
		{
			current = current->nextVegInfo;
			if (current == nullptr)
			{
				return nullptr;
			}
			else if (lambda(current))
			{
				return current;
			}
		}
		return current;
	}
	SecondaryNode* While(std::function<bool(SecondaryNode*)> lambda) {
		SecondaryNode* current1 = head;
		while (true)
		{
			current1 = current1->nextVegInfo;
			if (current1==nullptr)
			{
				break;
			}
			if (!lambda(current1))
			{
				if (current1->nextVegInfo==nullptr)
				{
					current1->prevVegInfo->nextVegInfo = nullptr;
					break;
				}
				else
				{
					current1->nextVegInfo->prevVegInfo = current1->prevVegInfo;
					current1->prevVegInfo->nextVegInfo = current1->nextVegInfo;
				}
			}
		}
		return head->nextVegInfo;
	}

	void Remove(int pos) {
		count--;
		if (pos == 0)
		{
			SecondaryNode* del = head->nextVegInfo;
			head->nextVegInfo = head->nextVegInfo->nextVegInfo;
			head->nextVegInfo->prevVegInfo = head;
			free(del);
			return;
		}

		SecondaryNode* prev = ElementAt(pos - 1);
		SecondaryNode* del = prev->nextVegInfo;
		prev->nextVegInfo = prev->nextVegInfo->nextVegInfo;
		prev->prevVegInfo = prev;
		free(del);
	}

	SecondaryNode* MallocSecondaryNode(int vegId, const char* vegName, const char* nutrtion) {

		SecondaryNode* node = (SecondaryNode*)malloc(sizeof(SecondaryNode)*2);
		node->vegId = vegId;
		node->nextVegInfo = nullptr;
		node->vegs = (TertiaryList*)malloc(sizeof(TertiaryList)*2);
		node->vegs->init();
		int len = strlen(vegName);
		for (size_t i = 0; i < len + 1; i++)
		{
			node->vegName[i] = vegName[i];
		}
		len = strlen(nutrtion);
		for (size_t i = 0; i < len + 1; i++)
		{
			node->nutrition[i] = nutrtion[i];
		}
		return node;
	}

	SecondaryNode* ElementAt(int pos) {
		SecondaryNode* current = head;
		while (true)
		{
			current = current->nextVegInfo;
			if (pos == 0)
			{
				break;
			}
			pos--;
		}
		return current;
	}

	SecondaryNode* MallocSecondaryNode() {

		SecondaryNode* node = (SecondaryNode*)malloc(sizeof(SecondaryNode));
		node->nextVegInfo = nullptr;
		node->vegs = nullptr;
		return node;
	}
};
struct PrimaryList
{
	PrimaryNode* head;
	int count = 0;
	PrimaryList() {
		this->head = MallocPrimaryNode('\0', nullptr);
		count = 0;
	}
	void Add(char classId, const char* className) {
		PrimaryNode* current = this->head;
		while (true)
		{
			if (current->nextVegClass == nullptr)
			{
				current->nextVegClass = MallocPrimaryNode(classId, className);
				current->nextVegClass->prevVegClass = current;
				break;
			}
			current = current->nextVegClass;
		}
		count++;
	}
	PrimaryNode* Where(std::function<bool(PrimaryNode*)> lambda) {
		PrimaryNode* current = head;
		while (true)
		{
			current = current->nextVegClass;
			if (current == nullptr)
			{
				return nullptr;
			}
			else if (lambda(current))
			{
				return current;
			}
		}
		return current;
	}
	PrimaryNode* While(std::function<bool(PrimaryNode*)> lambda) {
		PrimaryNode* current = head;
		while (true)
		{
			current = current->nextVegClass;
			if (current == nullptr)
			{
				break;
			}
			if (!lambda(current))
			{
				if (current->nextVegClass==nullptr)
				{
					current->prevVegClass->nextVegClass = nullptr;
					break;
				}
				else
				{
					current->prevVegClass->nextVegClass = current->nextVegClass;
					current->nextVegClass->prevVegClass = current->prevVegClass;
				}
			}
		}
		return head->nextVegClass;
	}
	void Remove(int pos) {
		count--;
		if (pos == 0)
		{
			PrimaryNode* del = head->nextVegClass;
			head->nextVegClass = head->nextVegClass->nextVegClass;
			head->nextVegClass->prevVegClass = head;
			free(del);
			return;
		}

		PrimaryNode* prev = ElementAt(pos - 1);
		PrimaryNode* del = prev->nextVegClass;
		prev->nextVegClass = prev->nextVegClass->nextVegClass;
		prev->nextVegClass->prevVegClass = prev;
		free(del);

	}
	PrimaryNode* ElementAt(int pos) {
		PrimaryNode* current = head;
		while (true)
		{
			current = current->nextVegClass;
			if (pos == 0)
			{
				break;
			}
			pos--;
		}
		return current;
	}

	PrimaryNode* MallocPrimaryNode(char classId, const char* className) {

		PrimaryNode* node = (PrimaryNode*)malloc(sizeof(PrimaryNode));
		node->classId = classId;
		node->nextVegClass = nullptr;
		node->vegInfos = (SecondaryList*)malloc(sizeof(SecondaryList));
		node->vegInfos->init();
		if (className == nullptr)
		{
			return node;
		}
		int len = strlen(className);
		for (size_t i = 0; i < len + 1; i++)
		{
			node->className[i] = className[i];
		}
		return node;
	}
};

其中的wile和when函数借用了c#中linq的设计思想,极大的提升了之后的开发效率。

读取与写入csv文件

由于c语言不存在反射操作,理论上无法使用纯c构造出泛用的序列化/反序列化方法。所以这次课设要求的csv读写操作只能自己一个一个实现。

读取csv

读取csv的时候要注意,csv的编码格式是utf-8 with bom,也就是说它文件开业有一个长度为4个char的padding,这四个字符是固定的。

void ParseReadStrings(char* str, char** classes, int* i) {
	char* temp = (char*)malloc(sizeof(char) * 100);
	*i = 0;
	int i1 = 0;
	int i2 = 0;
	while (true)
	{
		if (str[i1] == ',')
		{
			i1++;
			temp[i2] = '\0';
			i2 = 0;
			int tt = *i;

			classes[tt] = (char*)malloc(sizeof(char) * 100);
			strcpy(classes[tt], temp);
			(*i)++;
			continue;
		}
		if (str[i1] == '\n')
		{
			temp[i2] = '\0';
			classes[*i] = (char*)malloc(sizeof(char) * 100);
			strcpy(classes[*i], temp);
			(*i)++;
			break;
		}
		temp[i2] = str[i1];
		i1++;
		i2++;
	}
	free(temp);
}

void readCsv(PrimaryList* plist) {
	plantGid = 0;
	vegGid = 0;
	FILE* f = fopen("蔬菜种类信息表1.csv", "r");
	fflush(f);
	char** classes = (char**)malloc(sizeof(char*) * 100);
	char** classIds = (char**)malloc(sizeof(char*) * 100);
	char* del = (char*)malloc(sizeof(char) * 100);
	//去掉utf-8开头的编码
	fgets(del, 4, f);
	strcpy(padding, del);
	fgets(del, 1000, f);
	int i1 = 0;
	ParseReadStrings(del, classIds, &i1);
	char* str = (char*)malloc(sizeof(char) * 100);
	fflush(f);
	fgets(str, 1000, f);
	int i = 0;
	ParseReadStrings(str, classes, &i);
	for (size_t j = 0; j < i; j++)
	{
		plist->Add(classIds[j][0], classes[j]);
	}
	//for (size_t j = 0; j < i; j++)
	//{
	//	free(classes[j]);
	//	free(classIds[j]);
	//}
	free(classes);
	free(classIds);
	free(del);
	free(str);
	fclose(f);
	ReadSecondaryCsv(plist);
	ReadTertiaryCSV(plist);
}

void ReadSecondaryCsv(PrimaryList* plist) {
	FILE* f = fopen("蔬菜基本信息表1.csv", "r");
	fflush(f);
	char* del = (char*)malloc(sizeof(char) * 100);
	//去掉utf-8开头的编码
	fgets(del, 4, f);
	fgets(del, 1000, f);
	strcpy(head2, del);
	char* str = (char*)malloc(sizeof(char) * 100);
	fflush(f);
	char* classes[100];
	char* end;
	while (true)
	{
		end = fgets(str, 1000, f);
		if (end == nullptr)
		{
			break;
		}
		//free(end);
		int i = 0;
		ParseReadStrings(str, classes, &i);
		std::function<bool(PrimaryNode*)> func = [&](PrimaryNode* node) {
			return node->classId == classes[2][0];
		};
		PrimaryNode* node = plist->Where(func);
		node->vegInfos->Add(atoi(classes[0]) ,classes[1], classes[3]);
		for (size_t j = 0; j < i; j++)
		{
			free(classes[j]);
		}
		//free(end);
	}
	free(end);
	//free(classes);
	free(str);
	free(del);
	fclose(f);
}

void ReadTertiaryCSV(PrimaryList* plist) {
	FILE* f = fopen("菜农种植信息表1.csv", "r");
	fflush(f);
	char* del = (char*)malloc(sizeof(char) * 100);
	//去掉utf-8开头的编码
	fgets(del, 4, f);
	fgets(del, 1000, f);
	strcpy(head3, del);
	char* str = (char*)malloc(sizeof(char) * 100);
	fflush(f);
	char* classes[100];
	char* end;
	while (true)
	{
		end = fgets(str, 100, f);
		if (end == nullptr)
		{
			break;
		}
		//free(end);
		int i = 0;
		ParseReadStrings(str, classes, &i);
		auto secfunc = [&](SecondaryNode* node) {
			return node->vegId == atoi(classes[1]);
		};
		std::function<bool(PrimaryNode*)> func = [&](PrimaryNode* node) {
			auto re = node->vegInfos->Where(secfunc);
			return re != nullptr;
		};
		PrimaryNode* node = plist->Where(func);
		SecondaryNode* secNode = node->vegInfos->Where(secfunc);
		secNode->vegs->Add(atoi(classes[0]), atoi(classes[2]), atof(classes[3]), classes[4]);
		for (size_t j = 0; j < i; j++)
		{
			free(classes[j]);
		}
		//free(end);
	}
	free(end);
	//free(classes);
	free(str);
	free(del);
	fclose(f);
}

写入csv

void ParseSaveStrings(char* str, char** classes, int n) {
	char* temp = (char*)malloc(sizeof(char) * 1000);
	temp[0] = '\0';
	for (int i = 0; i < n; i++)
	{
		if (i != 0)
		{
			temp = strcat(temp, ",");
		}
		temp = strcat(temp, classes[i]);

	}
	temp = strcat(temp, "\n");
	strcpy(str, temp);
	free(temp);
}
void SavePrimaryCsv(PrimaryList* plist)
{
	FILE* f = fopen("蔬菜种类信息表1.csv", "w+");
	fflush(f);
	//char* dest = nullptr;
	PrimaryNode* node = plist->head;
	char* temp[10][10];
	int n = 0;
	while (true)
	{
		if (node->nextVegClass!=nullptr)
		{
			node = node->nextVegClass;
		}
		else
		{
			break;
		}
		temp[0][n] = (char*)malloc(sizeof(char) * 3);
		temp[0][n][0] = node->classId;
		temp[0][n][1] = '\0';
		temp[1][n] = (char*)malloc(sizeof(char) * 3);
		temp[1][n] = node->className;
		n++;
	}
	char* dest = (char*)malloc(sizeof(char) * 100);
	for (size_t i = 0; i < 2; i++)
	{
		ParseSaveStrings(dest, temp[i], n);
		if (i==0)
		{
			char* padtemp = (char*)malloc(sizeof(char) * 100);
			strcpy(padtemp, padding);
			strcpy(dest, strcat(padding, dest));
			strcpy(padding, padtemp);
		}
		fputs(dest, f);
		fflush(f);
	}
	fclose(f);
	SaveSecondaryCsv(plist);
	SaveTertiaryCsv(plist);
}
void SaveSecondaryCsv(PrimaryList* plist)
{
	FILE* f = fopen("蔬菜基本信息表1.csv", "w+");
	fflush(f);
	//char* dest = nullptr;
	PrimaryNode* node = plist->head;
	char* temp[10][10];
	int n = 0;
	while (true)
	{
		if (node->nextVegClass != nullptr)
		{
			node = node->nextVegClass;
		}
		else
		{
			break;
		}
		SecondaryNode* secnode = node->vegInfos->head;
		while (true)
		{
			if (secnode->nextVegInfo != nullptr)
			{
				secnode = secnode->nextVegInfo;
			}
			else
			{
				break;
			}
			temp[n][0] = (char*)malloc(sizeof(char) * 100);
			itoa(secnode->vegId, temp[n][0],10);
			temp[n][1] = (char*)malloc(sizeof(char) * 100);
			temp[n][1] = secnode->vegName;
			temp[n][2] = (char*)malloc(sizeof(char) * 100);
			temp[n][2][0]= node->classId;
			temp[n][2][1] = '\0';
			temp[n][3] = (char*)malloc(sizeof(char) * 100);
			temp[n][3] = secnode->nutrition;
			n++;
		}
	}
	char* padtemp = (char*)malloc(sizeof(char) * 100);
	char* dest = (char*)malloc(sizeof(char) * 100);
	strcpy(padtemp, padding);
	strcpy(dest, strcat(padding, head2));
	strcpy(padding, padtemp);
	fputs(dest, f);
	fflush(f);

	for (size_t i = 0; i < n; i++)
	{
		ParseSaveStrings(dest, temp[i], 4);
		fputs(dest, f);
		fflush(f);
	}
	fclose(f);
}

void SaveTertiaryCsv(PrimaryList* plist)
{
	FILE* f = fopen("菜农种植信息表1.csv", "w+");
	fflush(f);
	//char* dest = nullptr;
	PrimaryNode* node = plist->head;
	char* temp[10][10];
	int n = 0;
	while (true)
	{
		if (node->nextVegClass != nullptr)
		{
			node = node->nextVegClass;
		}
		else
		{
			break;
		}
		SecondaryNode* secnode = node->vegInfos->head;
		while (true)
		{
			if (secnode->nextVegInfo != nullptr)
			{
				secnode = secnode->nextVegInfo;
			}
			else
			{
				break;
			}
			TertiaryNode* ternode = secnode->vegs->head;
			while (true)
			{
				if (ternode->nextVeg != nullptr)
				{
					ternode = ternode->nextVeg;
				}
				else
				{
					break;
				}
				temp[n][0] = (char*)malloc(sizeof(char) * 100);
				itoa(ternode->id, temp[n][0], 10);
				temp[n][1] = (char*)malloc(sizeof(char) * 100);
				itoa(secnode->vegId, temp[n][1], 10);
				temp[n][2] = (char*)malloc(sizeof(char) * 100);
				itoa(ternode->area, temp[n][2], 10);
				temp[n][3] = (char*)malloc(sizeof(char) * 100);
				gcvt(ternode->weight, 10, temp[n][3]);
				temp[n][4] = (char*)malloc(sizeof(char) * 100);
				strcpy(temp[n][4], ternode->year);
				n++;
			}
		}
	}
	char* padtemp = (char*)malloc(sizeof(char) * 100);
	char* dest = (char*)malloc(sizeof(char) * 100);
	strcpy(padtemp, padding);
	strcpy(dest, strcat(padding, head3));
	strcpy(padding, padtemp);
	fputs(dest, f);
	fflush(f);

	for (size_t i = 0; i < n; i++)
	{
		ParseSaveStrings(dest, temp[i], 5);
		fputs(dest, f);
		fflush(f);
	}
	fclose(f);
}

导出编程接口

纵观各大cpp/c开发的数据库(redis、mongodb等),他们基本都是通过网络服务的形式提供接口。为什么呢?因为c/cpp和其它语言的交互能力十分有限。尽管一般可以相互传递基础的数据类型,但是很少能直接传递struct、class(clr cpp不算数,那个是 .Net家族成员,是托管代码了)。根据StackOverflow的说法,这是因为不同编译器存储struct的方法有微妙的区别,导致无法有统一的映射方法。
而这次课设,我的前端准备使用c#,所以必须提前想好c和c#的交互方式。 尽管伟大的Anders Hejlsberg在设计c#的时候考虑到这个问题,为我们提供了Marshal静态类,使用这个类的方法,c#可以自动映射cpp/c的结构体到c#中的结构体,但是这样做回使用到c#中的unsafe代码(因为涉及到类型指针),所以我决定一切使用最原始的方法进行,用IntPtr类来处理所有指针。
但是c#本身无法直接读取IntPtr指向的数据。所以我设计的数据读取流畅流程如下:

  1. c#调用c方法,获取结构体指针
    #define DllExport __declspec(dllexport)
    DllExport PrimaryNode* GetPrimary() {
        auto plist = PrimaryList();
        readCsv(&plist);
        return plist.head->nextVegClass;
    }
    
  2. c#获取指针后,吧指针传回c,获取指向的struct的某个字段的数据
    DllExport char GetPrimaryId(PrimaryNode* node) {
        return node->classId;
    }
    
  3. 重复2,知道获取了所有struct中的数据。

这其实是一个很笨的方法,却很有效。
最后只得一提的是c暴露编程接口的方法,要先extern "C"才行

extern "C"{
    DllExport PrimaryNode* GetPrimary() {
        auto plist = PrimaryList();
        readCsv(&plist);
        return plist.head->nextVegClass;
    }
    DllExport PrimaryNode* NextPrimary(PrimaryNode* node) {
        return node->nextVegClass;
    }
    DllExport char GetPrimaryId(PrimaryNode* node) {
        return node->classId;
    }
    DllExport char* GetPrimaryName(PrimaryNode* node) {
        return node->className;
    }
    DllExport SecondaryNode* GetSecondary(PrimaryNode* node) {
        return node->vegInfos->head->nextVegInfo;
    }
    DllExport SecondaryNode* NextSecondary(SecondaryNode* node) {
        return node->nextVegInfo;
    }
    DllExport int GetSecondaryId(SecondaryNode* node) {
        return node->vegId;
    }
    DllExport char* GetSecondaryName(SecondaryNode* node) {
        return node->vegName;
    }
    DllExport char* GetSecondaryNutrition(SecondaryNode* node) {
        return node->nutrition;
    }
    DllExport TertiaryNode* GetTertiary(SecondaryNode* node) {
        return node->vegs->head->nextVeg;
    }
    DllExport TertiaryNode* NextTertiary(TertiaryNode* node) {
        return node->nextVeg;
    }
    DllExport int GetTertiaryId(TertiaryNode* node) {
        return node->id;
    }
    DllExport int GetTertiaryArea(TertiaryNode* node) {
        return node->area;
    }
    DllExport char* GetTertiaryYear(TertiaryNode* node) {
        return node->year;
    }
    DllExport float GetTertiaryWeight(TertiaryNode* node) {
        return node->weight;
    }
    DllExport Heads* Search(char* key, PrimaryNode* p) {
        //FILE* f = fopen("string.txt", "r");
        //char a1[100];
        //fgets(a1, 100, f);
        //fflush(f);
        //fclose(f);

        FILE* f = fopen("string.txt", "w+");
        fputs(key, f);
        fflush(f);
        fclose(f);
        //key = toUTF8(key);
        auto headT = TertiaryNode();
        auto currentT = &headT;
        auto headS = SecondaryNode();
        auto currentS = &headS;
        auto plist1 = PrimaryList();
        plist1.head->nextVegClass = p;
        p->prevVegClass = plist1.head;
        std::function<bool(TertiaryNode*)> func3 = [&](TertiaryNode* node) {
            char a[100];
            auto b = contains(itoa(node->area, a, 10), key)
                || contains(itoa(node->weight, a, 10), key)
                || contains(gcvt(node->area, 10, a), key)
                || contains(node->year, key);
            if (b)
            {
                currentT->nextVeg = node;
                currentT = currentT->nextVeg;
            }
            return b;
        };
        std::function<bool(SecondaryNode*)> func2 = [&](SecondaryNode* node) {
            char a[100];
            auto inner = node->vegs->While(func3);
            auto b = contains(node->nutrition, key)
                || contains(node->vegName, key)
                || contains(itoa(node->vegId, a, 10), key);
            if (b)
            {
                currentS->nextVegInfo = node;
                currentS = currentS->nextVegInfo;
            }
            return b;
        };
        std::function<bool(PrimaryNode*)> func1 = [&](PrimaryNode* node) {
            auto inner = node->vegInfos->While(func2);
            return node->classId == key[0]
                || contains(node->className, key);
        };
        Heads* heads = (Heads*)malloc(100);
        heads->pnode = plist1.While(func1);
        currentS->nextVegInfo = nullptr;
        currentT->nextVeg = nullptr;
        heads->snode = headS.nextVegInfo;
        heads->tnode = headT.nextVeg;
        return heads;
    }
    DllExport PrimaryNode* GetPSearch(Heads* h) {
        return h->pnode;
    }
    DllExport SecondaryNode* GetSSearch(Heads* h) {
        return h->snode;
    }
    DllExport TertiaryNode* GetTSearch(Heads* h) {
        return h->tnode;
    }
    DllExport PrimaryNode* EditS(PrimaryNode* p, int vegId, char* vegName, char* nutrition, char classid) {
        auto plist1 = PrimaryList();
        plist1.head->nextVegClass = p;
        p->prevVegClass = plist1.head;
        SecondaryNode* snode;
        plist1.While([&](PrimaryNode* p) {
            p->vegInfos->While([&](SecondaryNode* s) {
                if (s->vegId == vegId) {
                    snode = s;
                    return false;
                }
                return true;
            });
            return true;
        });
        auto node = plist1.Where([&](PrimaryNode* p) {
            return p->classId == classid;
        });
        node->vegInfos->Add(vegId, vegName, nutrition);
        
        SavePrimaryCsv(&plist1);
        return p;
    }
    DllExport PrimaryNode* EditT(PrimaryNode* p, int id, int area, float weight, char* year) {
        auto plist1 = PrimaryList();
        plist1.head->nextVegClass = p;
        p->prevVegClass = plist1.head;
        SecondaryNode* snode;
        TertiaryNode* tnode;
        bool found = false;
        plist1.While([&](PrimaryNode* p) {
            p->vegInfos->While([&](SecondaryNode* s) {
                s->vegs->While([&](TertiaryNode* t) {
                    if (t->id==id)
                    {
                        tnode = t;
                        found = true;
                        return false;
                    }
                    return true;
                });
                if (found)
                {
                    snode = s;
                    found = false;
                }
                return true;
            });
            return true;
        });
        snode->vegs->Add(id, area, weight, year);

        SavePrimaryCsv(&plist1);
        return p;
    }
    DllExport PrimaryNode* EditP(PrimaryNode* p, char id, char* name) {
        auto plist1 = PrimaryList();
        plist1.head->nextVegClass = p;
        p->prevVegClass = plist1.head;
        auto node = plist1.Where([&](PrimaryNode* p) {
            return p->classId == id;
            });
        strcpy(node->className, name);
        SavePrimaryCsv(&plist1);
        return p;
    }
    DllExport PrimaryNode* DelP(PrimaryNode* p, char id) {
        auto plist1 = PrimaryList();
        plist1.head->nextVegClass = p;
        p->prevVegClass = plist1.head;
        plist1.While([&](PrimaryNode* p) {
                return p->classId != id;
            });
        SavePrimaryCsv(&plist1);
        return p;
    }
    DllExport PrimaryNode* DelT(PrimaryNode* p, int id) {
        auto plist1 = PrimaryList();
        plist1.head->nextVegClass = p;
        p->prevVegClass = plist1.head;
        plist1.While([&](PrimaryNode* p) {
            p->vegInfos->While([&](SecondaryNode* s) {
                s->vegs->While([&](TertiaryNode* t) {
                    if (t->id == id)
                    {
                        return false;
                    }
                    return true;
                    });
                return true;
                });
            return true;
            });

        SavePrimaryCsv(&plist1);
        return p;
    }
    DllExport PrimaryNode* DelS(PrimaryNode* p, int vegId) {
        auto plist1 = PrimaryList();
        plist1.head->nextVegClass = p;
        p->prevVegClass = plist1.head;
        plist1.While([&](PrimaryNode* p) {
            p->vegInfos->While([&](SecondaryNode* s) {
                if (s->vegId == vegId) {
                    return false;
                }
                return true;
                });
            return true;
            });

        SavePrimaryCsv(&plist1);
        return p;
    }
    DllExport PrimaryNode* AddP(PrimaryNode* p, char id, char* name) {
        auto plist1 = PrimaryList();
        plist1.head->nextVegClass = p;
        plist1.Add(id, name);
        SavePrimaryCsv(&plist1);
        return p;
    }
    DllExport PrimaryNode* AddS(PrimaryNode* p, int vegId, char* vegName, char* nutrition, char classid) {
        auto plist1 = PrimaryList();
        plist1.head->nextVegClass = p;
        p->prevVegClass = plist1.head;
        auto node = plist1.Where([&](PrimaryNode* p) {
            return p->classId == classid;
        });
        node->vegInfos->Add(vegId, vegName, nutrition);

        SavePrimaryCsv(&plist1);
        return p;
    }
    DllExport PrimaryNode* AddT(PrimaryNode* p, int id, int area, float weight, char* year, int vegId) {
        auto plist1 = PrimaryList();
        plist1.head->nextVegClass = p;
        p->prevVegClass = plist1.head;
        SecondaryNode* snode;
        plist1.While([&](PrimaryNode* p) {
            p->vegInfos->While([&](SecondaryNode* s) {
                if (s->vegId==vegId)
                {
                    snode = s;
                }
                return true;
            });
            return true;
            });
        snode->vegs->Add(id, area, weight, year);

        SavePrimaryCsv(&plist1);
        return p;
    }
}

使用pinvoke技术调用c的dll中的方法

这个没啥技术含量,就是要注意一点:c中的所有数组类型都是指针,字符串也是,要用IntPtr代替

public class VegC
{
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "Search")]
    private static extern IntPtr Search(IntPtr key, IntPtr pnode);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetPrimary")]
    private static extern IntPtr GetPrimary();
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "NextPrimary")]
    private static extern IntPtr NextPrimary(IntPtr node);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetPrimaryId")]
    private static extern char GetPrimaryId(IntPtr node);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetPrimaryName")]
    private static extern IntPtr GetPrimaryName(IntPtr node);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetSecondary")]
    private static extern IntPtr GetSecondary(IntPtr prime);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "NextSecondary")]
    private static extern IntPtr NextSecondary(IntPtr node);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetSecondaryId")]
    private static extern int GetSecondaryId(IntPtr node);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetSecondaryName")]
    private static extern IntPtr GetSecondaryName(IntPtr node);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetSecondaryNutrition")]
    private static extern IntPtr GetSecondaryNutrition(IntPtr node);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetTertiary")]
    private static extern IntPtr GetTertiary(IntPtr prime);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "NextTertiary")]
    private static extern IntPtr NextTertiary(IntPtr node);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetTertiaryId")]
    private static extern int GetTertiaryId(IntPtr node);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetTertiaryWeight")]
    private static extern float GetTertiaryWeight(IntPtr node);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetTertiaryArea")]
    private static extern int GetTertiaryArea(IntPtr node);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetTertiaryYear")]
    private static extern IntPtr GetTertiaryYear(IntPtr node);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetTSearch")]
    private static extern IntPtr GetTSearch(IntPtr node);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetSSearch")]
    private static extern IntPtr GetSSearch(IntPtr node);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetPSearch")]
    private static extern IntPtr GetPSearch(IntPtr node);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "EditP")]
    private static extern IntPtr EditP(IntPtr node, char id, IntPtr className);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "EditS")]
    private static extern IntPtr EditS(IntPtr node, int vegId, IntPtr vegName, IntPtr nutrition, char classId);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "EditT")]
    private static extern IntPtr EditT(IntPtr node, int id, int area, float weight, IntPtr year);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "AddP")]
    private static extern IntPtr AddP(IntPtr node, char id, IntPtr className);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "AddS")]
    private static extern IntPtr AddS(IntPtr node, int vegId, IntPtr vegName, IntPtr nutrition, char classId);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "AddT")]
    private static extern IntPtr AddT(IntPtr node, int id, int area, float weight, IntPtr year, int vegId);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "DelP")]
    private static extern IntPtr DelP(IntPtr node, char id);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "DelS")]
    private static extern IntPtr DelS(IntPtr node, int vegId);
    [DllImport("VegKS.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "DelT")]
    private static extern IntPtr DelT(IntPtr node, int id);
}

注意,我们这次的数据中有中文,中文是utf-8编码。所以字符串的IntPtr转化为c#中string的时候要用 Marshal.PtrToStringUTF8(IntPtr ptr)方法,同样传给c之前也要用Marshal.StringToCoTaskMemUTF8(string s)方法转换回IntPtr否则默认的编码是ansi,中文会变成乱码!

前端

操作前端使用Blazor技术,这里就不放代码了。总体而言没什么坑和技术含量。最后上几张效果图:
limfxcdn下载 limfxcdn下载 limfxcdn下载 limfxcdn下载

结语

实际上,很容易看出,老师为了考验我们,故意让这次的课设在设计上存在一些问题。
其中最大的问题,私以为莫过于十字链表存储数据。实际上,只要有数据结构设计/数据库操作经验的人就应该知道,十字链表并不适合这次的任务。这次任务中使用十字链表基本上只有炫技的优点。它的主要缺点是耦合过高

使用十字链表尽管能使数据冗余度降到最低,但是它带来了数据间极大的耦合度,这导致数据编辑算法异常的复杂。具体来说,编辑第一层节点时间复杂度 \(O(n)\) ,二级 \(O(n^2)\) ,三级 \(O(n^3)\) 。这就是为什么现代的关系型数据库使用多表结构而不是十字链表结构存储数据。实际上多表结构相比十字链表也就只多了外键的数据量。

源码

只提供c部分的源码
limfxcdn下载(zip)
github


本文章使用limfx的vsocde插件快速发布