第 4 章 串(图书关键字索引表实现)

1. 背景说明

需要从书目文件中获取其关键字及对应的书号索引

bookInfo.txt

005 Computer Data Structures
010 Introduction to Data Structures
023 Fundamentals of Data Structures
034 The Design and Analysis of Computer Algorithms
050 Introduction to Numerical Analysis
067 Numerical Analysis

normalWord.txt

5
A
An
In
Of
The

2. 示例代码

1) status.h

/* DataStructure 预定义常量和类型头文件 */#ifndef STATUS_H
#define STATUS_H#define CHECK_NULL(pointer) if (!(pointer)) { \printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ERR_NULL_PTR); \return NULL; \
}#define CHECK_RET(ret) if (ret != RET_OK) { \printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ret); \return ret; \
}#define CHECK_VALUE(value, ERR_CODE) if (value) { \printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ERR_CODE); \return ERR_CODE; \
}#define CHECK_FALSE(value, ERR_CODE) if (!(value)) { \printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ERR_CODE); \return FALSE; \
} /* 函数结果状态码 */
#define TRUE 					1			/* 返回值为真 */
#define FALSE 					0			/* 返回值为假 */
#define RET_OK 					0			/* 返回值正确 */
#define INFEASIABLE    		   	2			/* 返回值未知 */
#define ERR_MEMORY     		   	3			/* 访问内存错 */
#define ERR_NULL_PTR   			4			/* 空指针错误 */
#define ERR_MEMORY_ALLOCATE		5			/* 内存分配错 */
#define ERR_NULL_STACK			6			/* 栈元素为空 */
#define ERR_PARA				7			/* 函数参数错 */
#define ERR_OPEN_FILE			8			/* 打开文件错 */
#define ERR_NULL_QUEUE			9			/* 队列为空错 */
#define ERR_FULL_QUEUE			10			/* 队列为满错 */
#define ERR_NOT_FOUND			11			/* 表项不存在 */
typedef int Status;							/* Status 是函数的类型,其值是函数结果状态代码,如 RET_OK 等 */
typedef int Bollean;						/* Boolean 是布尔类型,其值是 TRUE 或 FALSE */#endif // !STATUS_H

2) linkList.h

/* 具有实用意义的线性链表(带头结点)实现头文件 */#ifndef LINKLIST_H
#define LINKLIST_H#include "status.h"typedef int ElemType;typedef struct LNode {ElemType data;struct LNode *next;
} LNode, *Link, *Position;typedef struct LinkList {Link head;Link tail;int length;
} LinkList;/* 分配由指向的值为 e 的结点,并返回节点地址;若分配失败, 则返回 NULL */
Link MakeNewLNode(ElemType e);/* 释放 p 所指结点 */
Status FreeLNode(Link *p);/* 构造一个空的线性链表 */
Status InitList(LinkList *list);/* 将线性链表 list 重置为空表,并释放原链表的结点空间 */
Status ClearList(LinkList *list);/* 销毁线性链表 list,list 不再存在 */
Status DestroyList(LinkList *list);/* head 指向 list 的一个结点,把 head 当做头结点,将 s 所指结点插入在第一个结点之前 */
Status InsFirst(Link head, Link s, LinkList *list);/* head 指向 list 的一个结点,把 head 当做头结点,删除链表中的第一个结点并以 q 返回若链表为空( head 指向尾结点),q = NULL */
Status DelFirst(Link head, Link *q, LinkList *list);/* 将指针 s(s->data 为第一个数据元素)所指(彼此以指针相链,以 NULL 结尾)的一串结点链接在线性链表 list 的最后一个结点之后,并改变链表 list 的尾指针指向新的尾结点 */
Status Append(Link s, LinkList *list);/* 已知 p 指向线性链表 list 中的一个结点,用 *targetPos 返回 p 所指结点的直接前驱的位置若无前驱则用 *targetPos 返回 NULL */
Status PriorPos(const Link p, const LinkList *list, Position *targetPos);/* 删除线性链表 list 中的尾结点并以 q 返回,改变链表 list 的尾指针指向新的尾结点 */
Status Remove(Link *q, LinkList *list);/* 已知 *p 指向线性链表 list 中的一个结点,将 s 所指结点插入在 *p 所指结点之前并修改指针 *p 指向新插入的结点 */
Status InsBefore(Link s, Link *p, LinkList *list);/* 已知 *p 指向线性链表 list 中的一个结点,将 s 所指结点插入在 *p 所指结点之后并修改指针 p 指向新插入的结点 */
Status InsAfter(Link s, Link *p, LinkList *list);/* 已知 p 指向线性链表中的一个结点,用 e 更新 p 所指结点中数据元素的值 */
Status SetCurrElem(ElemType e, Link p);/* 已知 p 指向线性链表中的一个结点,用 *e 返回 p 所指结点中数据元素的值 */
Status GetCurrElem(Link p, ElemType *e);/* 若线性链表 list 为空表,则用 *isEmpty 返回 TRUE,否则用 *isEmpty 返回 FALSE */
Status ListEmpty(const LinkList *list, Bollean *isEmpty);/* 用 *num 返回线性链表 list 中元素个数 */
Status ListLength(const LinkList *list, int *num);/* 用 *targetPos 返回线性链表 list 中头结点的位置 */
Status GetHead(const LinkList *L, Position *targetPos);/* 用 *targetPos 返回线性链表 list 中最后一个结点的位置 */
Status GetLast(const LinkList *L, Position *targetPos);/* 已知 p 指向线性链表 list 中的一个结点,用 *targetPos 返回 p 所指结点的直接后继的位置若无后继,则用 *targetPos 返回 NULL */
Status NextPos(Link p, Position *targetPos);/* 返回 p 指示线性链表 list 中第 i 个结点的位置,并返回 OK,i 值不合法时返回 ERROR, i = 0 为头结点 */
Status LocatePos(int i, const LinkList *list, Link *p);/* 用 *targetPos 返回线性链表 list 中第 1 个与 e 满足函数 compare() 判定关系的元素的位置若不存在这样的元素,则用 *targetPos 返回 NULL */
Status LocateElem(ElemType e, Bollean(*compare)(ElemType, ElemType), const LinkList *list, Position *targetPos);/* 依次对 list 的每个数据元素调用函数 visit()。一旦 visit() 失败,则操作失败 */
Status ListTraverse(void(*visit)(ElemType), const LinkList *list);/* 已知 list 为有序线性链表,将元素 e 按非降序插入在 list 中 */
Status InsertAscend(ElemType e, int(*compare)(ElemType, ElemType), LinkList *list);/* 若升序链表 list 中存在与 e 满足判定函数 compare() 取值为 0 的元素,则 q 指示 list 中第一个值为 e 的结点的位置,并用 *find 返回 TRUE;否则 q 指示第一个与 e 满足判定函数compare() 取值 > 0 的元素的前驱的位置, 并用 *find 返回 FALSE */
Status LocateElemP(ElemType e, int(*compare)(ElemType, ElemType), const LinkList *list, Bollean *find, Position *q);#endif // !LINKLIST_H

3) linkList.c

/* 具有实用意义的线性链表(带头结点)实现源文件 */#include "linkList.h"
#include <stdio.h>
#include <stdlib.h>/* 分配由指向的值为 e 的结点,并返回节点地址;若分配失败, 则返回 NULL */
Link MakeNewLNode(ElemType e)
{Link newLNode = (Link)malloc(sizeof(LNode));CHECK_NULL(newLNode);newLNode->data = e;newLNode->next = NULL;return newLNode;
}/* 释放 p 所指结点 */
Status FreeLNode(Link *p)
{CHECK_VALUE(!p || !(*p), ERR_NULL_PTR);free(*p);*p = NULL;return RET_OK;
}/* 构造一个空的线性链表 */
Status InitList(LinkList *list)
{CHECK_VALUE(!list, ERR_NULL_PTR);Link newLNode = (Link)malloc(sizeof(LNode));CHECK_VALUE(!newLNode, ERR_MEMORY_ALLOCATE);newLNode->next = NULL;list->head = list->tail = newLNode;list->length = 0;return RET_OK;
}/* 将线性链表 list 重置为空表,并释放原链表的结点空间 */
Status ClearList(LinkList *list)
{CHECK_VALUE(!list, ERR_NULL_PTR);if (list->head == list->tail) {return RET_OK;}Link p = list->head->next;list->head->next = NULL;Link q = NULL;while (p != list->tail) {q = p;p = p->next;free(q);}free(p);list->tail = list->head;list->length = 0;return RET_OK;
}/* 销毁线性链表 list,list 不再存在 */
Status DestroyList(LinkList *list)
{CHECK_VALUE(!list, ERR_NULL_PTR);ClearList(list);FreeLNode(&(list->head));list->tail = NULL;return RET_OK;
}/* head 指向 list 的一个结点,把 head 当做头结点,将 s 所指结点插入在第一个结点之前 */
Status InsFirst(Link head, Link s, LinkList *list)
{CHECK_VALUE(!head || !s || !list, ERR_NULL_PTR);s->next = head->next;head->next = s;if (head == list->tail) {list->tail = s;}++(list->length);return RET_OK;
}/* head 指向 list 的一个结点,把 head 当做头结点,删除链表中的第一个结点并以 q 返回若链表为空( head 指向尾结点),q = NULL */
Status DelFirst(Link head, Link *q, LinkList *list)
{CHECK_VALUE(!head || !q || !(*q) || !list, ERR_NULL_PTR);*q = head->next;if (!(*q)) {return RET_OK;}head->next = (*q)->next;--(list->length);if (!(head->next)) {list->tail = head;}return RET_OK;
}/* 将指针 s(s->data 为第一个数据元素)所指(彼此以指针相链,以 NULL 结尾)的一串结点链接在线性链表 list 的最后一个结点之后,并改变链表 list 的尾指针指向新的尾结点 */
Status Append(Link s, LinkList *list)
{CHECK_VALUE(!s || !list, ERR_NULL_PTR);int length = 1;list->tail->next = s;while (s->next) {++length;s = s->next;}list->tail = s;list->length += length;return RET_OK;
}/* 已知 p 指向线性链表 list 中的一个结点,用 *targetPos 返回 p 所指结点的直接前驱的位置若无前驱则用 *targetPos 返回 NULL */
Status PriorPos(const Link p, const LinkList *list, Position *targetPos)
{CHECK_VALUE(!p || !list || !targetPos, ERR_NULL_PTR);Link q = list->head->next;if (p == q) {*targetPos = NULL;return RET_OK;}while (q->next != p) {q = q->next;}*targetPos = q;return RET_OK;
}/* 删除线性链表 list 中的尾结点并以 q 返回,改变链表 list 的尾指针指向新的尾结点 */
Status Remove(Link *q, LinkList *list)
{CHECK_VALUE(!q || !(*q) || !list, ERR_NULL_PTR);if (list->length == 0) {*q = NULL;return RET_OK;}*q = list->tail;Link p = list->head;while (p->next != list->tail) {p = p->next;}p->next = NULL;list->tail = p;--(list->length);return RET_OK;
}/* 已知 *p 指向线性链表 list 中的一个结点,将 s 所指结点插入在 *p 所指结点之前并修改指针 *p 指向新插入的结点 */
Status InsBefore(Link s, Link *p, LinkList *list)
{CHECK_VALUE(!s || !p || !(*p) || !list, ERR_NULL_PTR);Link q;PriorPos(*p, list, &q);if (!q) {q = list->head;}s->next = *p;q->next = s;*p = s;++(list->length);return RET_OK;
}/* 已知 *p 指向线性链表 list 中的一个结点,将 s 所指结点插入在 *p 所指结点之后并修改指针 p 指向新插入的结点 */
Status InsAfter(Link s, Link *p, LinkList *list)
{CHECK_VALUE(!s || !p || !(*p) || !list, ERR_NULL_PTR);if (*p == list->tail) {(*list).tail = s;}s->next = (*p)->next;(*p)->next = s;*p = s;++(list->length);return RET_OK;
}/* 已知 p 指向线性链表中的一个结点,用 e 更新 p 所指结点中数据元素的值 */
Status SetCurrElem(ElemType e, Link p)
{CHECK_VALUE(!p, ERR_NULL_PTR);p->data = e;return RET_OK;
}/* 已知 p 指向线性链表中的一个结点,用 *e 返回 p 所指结点中数据元素的值 */
Status GetCurrElem(Link p, ElemType *e)
{CHECK_VALUE(!p, ERR_NULL_PTR);*e = p->data;return RET_OK;
}/* 若线性链表 list 为空表,则用 *isEmpty 返回 TRUE,否则用 *isEmpty 返回 FALSE */
Status ListEmpty(const LinkList *list, Bollean *isEmpty)
{CHECK_VALUE(!list || !isEmpty, ERR_NULL_PTR);*isEmpty = (list->length == 0) ? TRUE : FALSE;return RET_OK;
}/* 用 *num 返回线性链表 list 中元素个数 */
Status ListLength(const LinkList *list, int *num)
{CHECK_VALUE(!list || !num, ERR_NULL_PTR);*num = list->length;return RET_OK;
}/* 用 *targetPos 返回线性链表 list 中头结点的位置 */
Status GetHead(const LinkList *L, Position *targetPos)
{CHECK_VALUE(!L || !targetPos, ERR_NULL_PTR);*targetPos = L->head;return RET_OK;
}/* 用 *targetPos 返回线性链表 list 中最后一个结点的位置 */
Status GetLast(const LinkList *L, Position *targetPos)
{CHECK_VALUE(!L || !targetPos, ERR_NULL_PTR);*targetPos = L->tail;return RET_OK;
}/* 已知 p 指向线性链表 list 中的一个结点,用 *targetPos 返回 p 所指结点的直接后继的位置若无后继,则用 *targetPos 返回 NULL */
Status NextPos(Link p, Position *targetPos)
{CHECK_VALUE(!p || !targetPos, ERR_NULL_PTR);*targetPos = p->next;return RET_OK;
}/* 返回 p 指示线性链表 list 中第 i 个结点的位置,并返回 OK,i 值不合法时返回 ERROR, i = 0 为头结点 */
Status LocatePos(int i, const LinkList *list, Link *p)
{CHECK_VALUE(!list || !p, ERR_NULL_PTR);CHECK_VALUE((i < 0 || i > list->length), ERR_PARA);*p = list->head;for (int j = 0; j < i; ++j) {*p = (*p)->next;}return RET_OK;
}/* 用 *targetPos 返回线性链表 list 中第 1 个与 e 满足函数 compare() 判定关系的元素的位置若不存在这样的元素,则用 *targetPos 返回 NULL */
Status LocateElem(ElemType e, Bollean(*compare)(ElemType, ElemType), const LinkList *list, Position *targetPos)
{CHECK_VALUE(!compare || !list || !targetPos, ERR_NULL_PTR);Link p = list->head->next;while ((p) && !(compare(p->data, e))) {p = p->next;}*targetPos = p;return RET_OK;
}/* 依次对 list 的每个数据元素调用函数 visit()。一旦 visit() 失败,则操作失败 */
Status ListTraverse(void(*visit)(ElemType), const LinkList *list)
{CHECK_VALUE(!visit || !list, ERR_NULL_PTR);Link p = list->head->next;for (int i = 0; i < list->length; ++i) {visit(p->data);p = p->next;}return RET_OK;
}/* 已知 list 为有序线性链表,将元素 e 按非降序插入在 list 中 */
Status InsertAscend(ElemType e, int(*compare)(ElemType, ElemType), LinkList *list)
{CHECK_VALUE(!compare || !list, ERR_NULL_PTR);Link q = list->head;Link p = q->next;while ((p) && (compare(p->data, e) < 0)) {q = p;p = p->next;}Link newLNode = MakeNewLNode(e);CHECK_VALUE(!newLNode, ERR_NULL_PTR);q->next = newLNode;newLNode->next = p;++(list->length);if (!p) {list->tail = newLNode;}return RET_OK;
}/* 若升序链表 list 中存在与 e 满足判定函数 compare() 取值为 0 的元素,则 q 指示 list 中第一个值为 e 的结点的位置,并用 *find 返回 TRUE;否则 q 指示第一个与 e 满足判定函数compare() 取值 > 0 的元素的前驱的位置, 并用 *find 返回 FALSE */
Status LocateElemP(ElemType e, int(*compare)(ElemType, ElemType), const LinkList *list, Bollean *find, Position *q)
{CHECK_VALUE(!compare || !list || !find || !q, ERR_NULL_PTR);Link pos = list->head;Link p = pos->next;while ((p) && (compare(p->data, e) < 0)) {pos = p;p = p->next;}if ((!p) || (compare(p->data, e) > 0)) {*q = pos;*find = FALSE;}*q = p;*find = TRUE;return RET_OK;
}

4) hString.h

/* 串的堆分配存储实现头文件 */#ifndef HSTRING_H
#define HSTRING_H#include "status.h"typedef struct {char *ch;int length;
} HString;/* 初始化(产生空串)字符串 t */
Status InitString(HString *t);/* 生成一个其值等于串常量 chars 的串 t */
Status StrAssign(const char *chars, HString *t);/* 初始条件: 串 s 存在操作结果: 由串 s 复制得串 t */
Status StrCopy(const HString *s, HString *t);/* 初始条件: 串 s 存在操作结果: 若 s 为空串,则用 *isEmpty 返回 TRUE,否则用 *isEmpty 返回 FALSE */
Status StrEmpty(const HString *s, Bollean *isEmpty);/* 若 s > t,则返回值 > 0;若 s = t,则返回值 = 0;若 s < t,则返回值 < 0 */
Status StrCompare(const HString *s, const HString *t, int *ret);/* 返回 s 的元素个数,称为串的长度 */
Status StrLength(const HString *s, int *length);/* 将 s 清为空串 */
Status ClearString(HString *s);/* 用 t 返回由 s1 和 s2 联接而成的新串 */
Status Concat(const HString *s1, const HString *s2, HString *t);/* 用 sub 返回串 s 的第 pos 个字符起长度为 len 的子串其中,1 ≤ pos ≤ sLength 且 0 ≤ len ≤ sLength - pos + 1 */
Status SubString(int pos, int len, const HString *s, HString *sub);/* 算法 4.1,t 为非空串。若主串 s 中第 pos 个字符之后存在与 t 相等的子串则返回第一个这样的子串在 s 中的位置,否则返回 0 */
Status Index(int pos, const HString *s, const HString *t, int *targetPos);/* 算法 4.4, 在串 s 的第 pos 个字符之前插入串 t, 1 ≤ pos ≤ sLength + 1 */
Status StrInsert(int pos, const HString *t, HString *s);/* 从串 s 中删除第 pos 个字符起长度为 len 的子串 */
Status StrDelete(int pos, int len, HString *s);/* 初始条件: 串 s, t 和 v 存在, t 是非空串(此函数与串的存储结构无关)操作结果: 用 v 替换主串 s 中出现的所有与 t 相等的不重叠的子串 */
Status Replace(const HString *t, const HString *v, HString *s);/* 堆分配类型的字符串无法销毁(结构体)*/
void DestroyString(void);/* 输出 t 字符串 */
Status StrPrint(const HString *t);#endif // !HSTRING_H

5) hString.c

/* 串的堆分配存储实现源文件 */#include "hString.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>/* 初始化(产生空串)字符串 t */
Status InitString(HString *t)
{CHECK_VALUE(!t, ERR_NULL_PTR);t->length = 0;t->ch = NULL;return RET_OK;
}/* 生成一个其值等于串常量 chars 的串 t */
Status StrAssign(const char *chars, HString *t)
{CHECK_VALUE(!chars || !t, ERR_NULL_PTR);if (t->ch) {free(t->ch);}int length = (int)strlen(chars);if (length == 0) {t->ch = NULL;t->length = 0;return RET_OK;}t->ch = (char *)malloc(sizeof(char) * length);CHECK_VALUE(!(t->ch), ERR_MEMORY_ALLOCATE);t->length = length;for (int i = 0; i < length; ++i) {t->ch[i] = chars[i];}return RET_OK;
}/* 初始条件: 串 s 存在操作结果: 由串 s 复制得串 t */
Status StrCopy(const HString *s, HString *t)
{CHECK_VALUE(!s || !t, ERR_NULL_PTR);if (t->ch) {free(t->ch);}t->ch = (char *)malloc(sizeof(char) * (s->length));CHECK_VALUE(!(t->ch), ERR_MEMORY_ALLOCATE);t->length = s->length;for (int i = 0; i < s->length; ++i) {t->ch[i] = s->ch[i];}return RET_OK;
}/* 初始条件: 串 s 存在操作结果: 若 s 为空串,则用 *isEmpty 返回 TRUE,否则用 *isEmpty 返回 FALSE */
Status StrEmpty(const HString *s, Bollean *isEmpty)
{CHECK_VALUE(!s || isEmpty, ERR_NULL_PTR);*isEmpty = ((s->length == 0) && (s->ch == NULL)) ? TRUE : FALSE;return RET_OK;
}/* 若 s > t,则返回值 > 0;若 s = t,则返回值 = 0;若 s < t,则返回值 < 0 */
Status StrCompare(const HString *s, const HString *t, int *ret)
{CHECK_VALUE(!s || !t || !ret, ERR_NULL_PTR);for (int i = 0; (i < s->length) && (i < t->length); ++i) {if (s->ch[i] != t->ch[i]) {*ret = s->ch[i] - t->ch[i];return RET_OK;}}*ret = s->length - t->length;return RET_OK;
}/* 返回 s 的元素个数,称为串的长度 */
Status StrLength(const HString *s, int *length)
{CHECK_VALUE(!s || !length, ERR_NULL_PTR);*length = s->length;return RET_OK;
}/* 将 s 清为空串 */
Status ClearString(HString *s)
{CHECK_VALUE(!s, ERR_NULL_PTR);if (s->ch) {free(s->ch);s->ch = NULL;}s->length = 0;return RET_OK;
}/* 用 t 返回由 s1 和 s2 联接而成的新串 */
Status Concat(const HString *s1, const HString *s2, HString *t)
{CHECK_VALUE(!s1 || !s2 || !t, ERR_NULL_PTR);if (t->ch) {free(t->ch);}int length = s1->length + s2->length;t->ch = (char *)malloc(sizeof(char) * length);CHECK_VALUE(!(t->ch), ERR_MEMORY_ALLOCATE);t->length = length;for (int i = 0; i < s1->length; ++i) {t->ch[i] = s1->ch[i];}for (int i = 0; i < s2->length; ++i) {t->ch[s1->length + i] = s2->ch[i];}return RET_OK;
}/* 用 sub 返回串 s 的第 pos 个字符起长度为 len 的子串其中,1 ≤ pos ≤ sLength 且 0 ≤ len ≤ sLength - pos + 1 */
Status SubString(int pos, int len, const HString *s, HString *sub)
{CHECK_VALUE(!s || !sub, ERR_NULL_PTR);CHECK_VALUE((pos < 1) || (pos > s->length) || (len < 1) || (len > s->length - pos + 1), ERR_PARA);if (sub->ch) {free(sub->ch);}if (len == 0) {sub->ch = NULL;sub->length = 0;} else {sub->ch = (char *)malloc(sizeof(char) * len);CHECK_VALUE(!(sub->ch), ERR_MEMORY_ALLOCATE);for (int i = 0; i < len; ++i) {sub->ch[i] = s->ch[pos - 1 + i];}sub->length = len;}return RET_OK;
}/* 算法 4.1,t 为非空串。若主串 s 中第 pos 个字符之后存在与 t 相等的子串则返回第一个这样的子串在 s 中的位置,否则返回 0 */
Status Index(int pos, const HString *s, const HString *t, int *targetPos)
{CHECK_VALUE(!s || !t || !targetPos, ERR_NULL_PTR);/* 模块内部调用,不检查返回值,外部调用需要检查 */int sLength, tLength;StrLength(s, &sLength);StrLength(t, &tLength);int maxRange = sLength - tLength + 1;CHECK_VALUE((pos < 1) || (pos > maxRange), ERR_PARA);HString sub;InitString(&sub);Status subRet;int ret;while (pos <= maxRange) {subRet = SubString(pos, tLength, s, &sub);CHECK_VALUE(subRet != RET_OK, subRet);StrCompare(s, t, &ret);if (ret != 0) {++pos;} else {*targetPos = pos;return RET_OK;}}*targetPos = 0;return RET_OK;
}/* 算法 4.4, 在串 s 的第 pos 个字符之前插入串 t, 1 ≤ pos ≤ sLength + 1 */
Status StrInsert(int pos, const HString *t, HString *s)
{CHECK_VALUE(!t || !s, ERR_NULL_PTR);CHECK_VALUE((pos < 1) || (pos > s->length + 1), ERR_PARA);if (s->length == 0) {return RET_OK;}s->ch = (char *)realloc(s->ch, sizeof(char) * (unsigned long long)(s->length + s->length));CHECK_VALUE(!(s->ch), ERR_MEMORY_ALLOCATE);for (int i = s->length - 1; i >= pos - 1; --i) {s->ch[i + t->length] = s->ch[i];}s->length += t->length;for (int i = 0; i < t->length; ++i) {s->ch[pos - 1 + i] = t->ch[i];}return RET_OK;
}/* 从串 s 中删除第 pos 个字符起长度为 len 的子串 */
Status StrDelete(int pos, int len, HString *s)
{CHECK_VALUE(!s, ERR_NULL_PTR);CHECK_VALUE((pos < 1) || (pos > s->length) || (len < 1) || (len > s->length - pos + 1), ERR_PARA);for (int i = pos - 1; i < s->length - len; ++i) {s->ch[i] = s->ch[i + len];}s->ch = (char *)realloc(s->ch, sizeof(char) * s->length);CHECK_VALUE(!s->ch, ERR_MEMORY_ALLOCATE);s->length -= len;return RET_OK;
}/* 初始条件: 串 s, t 和 v 存在, t 是非空串(此函数与串的存储结构无关)操作结果: 用 v 替换主串 s 中出现的所有与 t 相等的不重叠的子串 */
Status Replace(const HString *t, const HString *v, HString *s)
{CHECK_VALUE(!t || !v || !s, ERR_NULL_PTR);int pos = 1, targetPos, tLength, vLength;StrLength(t, &tLength);StrLength(v, &vLength);Status ret;do {Index(pos, s, t, &targetPos);pos = targetPos;if (pos) {ret = StrDelete(pos, tLength, s);CHECK_VALUE(ret != RET_OK, ret);ret = StrInsert(pos, t, s);CHECK_VALUE(ret != RET_OK, ret);pos += vLength;}} while (pos);return RET_OK;
}/* 堆分配类型的字符串无法销毁(结构体)*/
void DestroyString(void)
{printf("Do not need to destroy!\n");
}/* 输出 t 字符串 */
Status StrPrint(const HString *t)
{CHECK_VALUE(!t, ERR_NULL_PTR);for (int i = 0; i < t->length; ++i) {putchar(t->ch[i]);}return RET_OK;
}

6) bookNameSort.h

/* 关键字索引表定义头文件 */#ifndef BOOKNAMESORT_H
#define BOOKNAMESORT_H#include "hString.h"
#include "linkList.h"#define MAX_KEY_NUM 25			/* 索引表的最大容量(关键词的最大数) */
#define MAX_LINE_LEN 100		/* 书目串(书名 + 书号) buff 的最大长度 */
#define MAX_KEY_WORD_LEN 15		/* 关键字最大长度 */
#define MAX_WORD_NUM 10			/* 词表(一本书的关键词)的最大容量 */
#define MAX_NORMAL_NUM 10		/* 常用词(仅指大写)的最大数 */
#define MAX_BOOK_NUM 10			/* 最大书数量 */typedef struct {char *item[MAX_WORD_NUM];int last;
} WordListType;typedef struct {char *item[MAX_NORMAL_NUM];int last;
} NormalIdxType;typedef struct {HString key;LinkList bookNumList;
} IdxTermType;typedef struct {IdxTermType item[MAX_KEY_NUM];int last;
} IdxListType;typedef struct {char bookName[MAX_LINE_LEN];int bookNum;
} BookTermType;typedef struct {BookTermType item[MAX_BOOK_NUM];int last;
} BookListType;/* 初始化操作,置索引表 idxlist 为空表,且在 idxliat.item[0] 设一空串 */
Status InitIdxList(IdxListType *idxList);Status ExtractKeyWord(const char *buff, const NormalIdxType *normalIdx, int *bookNum, WordListType *wordList);/* 用 keyWord 返回词表 wordList 中第 i 个关键词 */
Status GetKeyWord(int i, const WordListType *wordList, HString *keyWord);/* 在索引表 idxList 中查询是否存在与 keyWord 相等的关键词。若存在,则用 *pos 返回其在索引表中的位置, 且 *exist 取值 TRUE; 否则用 *pos 返回插入位置,且 *exist 取值 FALSE */
Status LocateIdx(const IdxListType *idxList, const HString *keyWord, Bollean *exist, int *pos);/* 在索引表 idxList 的第 i 项前插入新关键词 keyWord,并初始化书号索引的链表为空表 */
Status InsertNewKeyWord(int i, const HString *keyWord, IdxListType *idxList);/* 在索引表 idxList 的第 i 项前插入书号为 bookNum 的索引 */
Status InsertBookNum(int i, int bookNum, IdxListType *idxList);/* 将书号为 bookNum 的关键词插入索引表 */
Status InsIdxList(int bookNum, const WordListType *wordList, IdxListType *idxList);/* 将生成的索引表 idxList 输出到文件 file */
Status SaveToFile(const char *fileName, const IdxListType *idxList);#endif // !BOOKNAMESORT_H

7) bookNameSort.c

/* 关键字索引表实现源文件 */#include "bookNameSort.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>/* 初始化操作,置索引表 idxlist 为空表,且在 idxliat.item[0] 设一空串 */
Status InitIdxList(IdxListType *idxList)
{CHECK_VALUE(!idxList, ERR_NULL_PTR);idxList->last = 0;return RET_OK;
}Status ExtractKeyWord(const char *buff, const NormalIdxType *normalIdx, int *bookNum, WordListType *wordList)
{CHECK_VALUE(!buff || !normalIdx || !bookNum || !wordList, ERR_NULL_PTR);CHECK_VALUE(((int)strlen(buff) > MAX_LINE_LEN) || (buff[0] < '0') || (buff[0] > '9'), ERR_PARA);for (int i = 0; i < wordList->last; ++i) {free(wordList->item[i]);wordList->item[i] = NULL;}wordList->last = 0;*bookNum = (buff[0] - '0') * 100 + (buff[1] - '0') * 10 + (buff[2] - '0');const char *s1, *s2 = buff + 3;Bollean getEnd = FALSE;int length = 0;int i = 0;do {s1 = s2 + 1;s2 = strchr(s1, ' ');if (s2 == NULL) {s2 = strchr(s1, '\12');getEnd = TRUE;}length = (int)(s2 - s1);CHECK_VALUE(length > MAX_KEY_WORD_LEN, ERR_PARA);if (isupper(s1[0])) {wordList->item[wordList->last] = (char *)malloc(sizeof(char) * (unsigned long long)(length + 1));CHECK_VALUE(!(wordList->item[wordList->last]), ERR_MEMORY_ALLOCATE);for (i = 0; i < length; ++i) {wordList->item[wordList->last][i] = s1[i];}wordList->item[wordList->last][length] = '\0';for (i = 0; i < normalIdx->last; ++i) {if (strcmp(wordList->item[wordList->last], normalIdx->item[i]) == 0) {break;}}if (i != normalIdx->last) {free(wordList->item[wordList->last]);wordList->item[wordList->last] = NULL;continue;}++(wordList->last);}} while (!getEnd);return RET_OK;
}/* 用 keyWord 返回词表 wordList 中第 i 个关键词 */
Status GetKeyWord(int i, const WordListType *wordList, HString *keyWord)
{CHECK_VALUE(!wordList || !keyWord, ERR_NULL_PTR);CHECK_VALUE((i < 1) || (i > wordList->last + 1), ERR_PARA);Status ret = StrAssign(wordList->item[i - 1], keyWord);CHECK_VALUE(ret != RET_OK, ret);return RET_OK;
}/* 在索引表 idxList 中查询是否存在与 keyWord 相等的关键词。若存在,则用 *pos 返回其在索引表中的位置, 且 *exist 取值 TRUE; 否则用 *pos 返回插入位置,且 *exist 取值 FALSE */
Status LocateIdx(const IdxListType *idxList, const HString *keyWord, Bollean *exist, int *pos)
{CHECK_VALUE(!idxList || !keyWord || !exist || !pos, ERR_NULL_PTR);Status ret, retVal;for (int i = 0; i < idxList->last; ++i) {retVal = StrCompare(keyWord, &(idxList->item[i].key), &ret);CHECK_VALUE(retVal != RET_OK, retVal);if (ret == 0) {*exist = TRUE;*pos = i + 1;return RET_OK;}if (ret < 0) {*exist = FALSE;*pos = i + 1;return RET_OK;}}*exist = FALSE;*pos = idxList->last + 1;return RET_OK;
}/* 在索引表 idxList 的第 i 项前插入新关键词 keyWord,并初始化书号索引的链表为空表 */
Status InsertNewKeyWord(int i, const HString *keyWord, IdxListType *idxList)
{CHECK_VALUE(!keyWord || !idxList, ERR_NULL_PTR);CHECK_VALUE((i < 1) || (i > idxList->last + 1) || (idxList->last == MAX_KEY_NUM), ERR_PARA);errno_t errRet;for (int j = idxList->last; j >= i; --j) {errRet = memcpy_s(&(idxList->item[j]), sizeof(IdxTermType), &(idxList->item[j - 1]), sizeof(IdxTermType));CHECK_VALUE(errRet != 0, errRet);}Status ret = InitString(&(idxList->item[i - 1].key));CHECK_VALUE(ret != RET_OK, ret);ret = StrCopy(keyWord, &(idxList->item[i - 1].key));CHECK_VALUE(ret != RET_OK, ret);ret = InitList(&(idxList->item[i - 1].bookNumList));CHECK_VALUE(ret != RET_OK, ret);++(idxList->last);return RET_OK;
}/* 在索引表 idxList 的第 i 项前插入书号为 bookNum 的索引 */
Status InsertBookNum(int i, int bookNum, IdxListType *idxList)
{CHECK_VALUE(!idxList, ERR_NULL_PTR);CHECK_VALUE((i < 1) || (i > idxList->last + 1), ERR_PARA);Link newLNode = MakeNewLNode(bookNum);CHECK_VALUE(!newLNode, ERR_MEMORY_ALLOCATE);Status ret = Append(newLNode, &(idxList->item[i - 1].bookNumList));CHECK_VALUE(ret != RET_OK, ret);return RET_OK;
}/* 将书号为 bookNum 的关键词插入索引表 */
Status InsIdxList(int bookNum, const WordListType *wordList, IdxListType *idxList)
{HString keyWord = { 0 };int pos;Bollean exist;Status ret = 0;for (int i = 0; i < wordList->last; ++i) {ret = GetKeyWord(i + 1, wordList, &keyWord);CHECK_VALUE(ret != RET_OK, ret);ret = LocateIdx(idxList, &keyWord, &exist, &pos);CHECK_VALUE(ret != RET_OK, ret);if (!exist) {ret = InsertNewKeyWord(pos, &keyWord, idxList);CHECK_VALUE(ret != RET_OK, ret);}ret = InsertBookNum(pos, bookNum, idxList);CHECK_VALUE(ret != RET_OK, ret);}return RET_OK;
}/* 将生成的索引表 idxList 输出到文件 file */
Status SaveToFile(const char *fileName, const IdxListType *idxList)
{CHECK_VALUE(!fileName || !idxList, ERR_NULL_PTR);FILE *fp;errno_t err_ret = fopen_s(&fp, fileName, "w");CHECK_VALUE(err_ret != RET_OK, ERR_OPEN_FILE);fprintf_s(fp, "%d\n", idxList->last);for (int i = 0; i < idxList->last; ++i) {idxList->item[i].key.ch[0] = tolower(idxList->item[i].key.ch[0]);for (int j = 0; j < idxList->item[i].key.length; ++j) {fprintf_s(fp, "%c", idxList->item[i].key.ch[j]);}fprintf_s(fp, "\n%d\n", idxList->item[i].bookNumList.length);Link p = idxList->item[i].bookNumList.head;for (int j = 0; j < idxList->item[i].bookNumList.length; ++j) {p = p->next;fprintf_s(fp, "%d ", p->data);}fprintf_s(fp, "\n");}fclose(fp);return RET_OK;
}

8) main.c

#include "bookNameSort.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>int main(void)
{/* Step 1 */FILE *fp;errno_t err_ret = fopen_s(&fp, "normalWord.txt", "r");CHECK_VALUE(err_ret != RET_OK, ERR_OPEN_FILE);NormalIdxType normalIdx = { 0 };fscanf_s(fp, "%d", &normalIdx.last);char buff[MAX_KEY_WORD_LEN + 1];unsigned int length;for (int i = 0; i < normalIdx.last; ++i) {fscanf_s(fp, "%s", buff, MAX_KEY_WORD_LEN + 1);length = (int)strlen(buff) + 1;normalIdx.item[i] = (char *)malloc(sizeof(char) * length);strcpy_s(normalIdx.item[i], sizeof(char) * length, buff);}fclose(fp);err_ret = fopen_s(&fp, "bookInfo.txt", "r");CHECK_VALUE(err_ret != RET_OK, ERR_OPEN_FILE);WordListType wordList;int bookNum;Status ret;IdxListType idxList;InitIdxList(&idxList);char bookInfoBuff[MAX_LINE_LEN];BookListType bookList = { 0 };int bookCount = 0;while (fgets(bookInfoBuff, MAX_LINE_LEN, fp) != NULL) {ret = ExtractKeyWord(bookInfoBuff, &normalIdx, &bookNum, &wordList);CHECK_VALUE(ret != RET_OK, ret);ret = InsIdxList(bookNum, &wordList, &idxList);CHECK_VALUE(ret != RET_OK, ret);bookInfoBuff[(int)(strlen(bookInfoBuff)) - 1] = '\0';bookList.item[bookCount].bookNum = (bookInfoBuff[0] - '0') * 100 + (bookInfoBuff[1] - '0') * 10 +(bookInfoBuff[2] - '0');err_ret = strcpy_s(bookList.item[bookCount].bookName, sizeof(bookList.item[bookCount].bookName),bookInfoBuff);CHECK_VALUE(err_ret != RET_OK, err_ret);++bookCount;}bookList.last = bookCount;fclose(fp);ret = SaveToFile("idxList.txt", &idxList);CHECK_VALUE(ret != RET_OK, ret);/* Step 2 */printf("Please input one key word: ");scanf_s("%s", buff, MAX_KEY_WORD_LEN + 1);int i = 0;while (buff[i]) {buff[i] = tolower(buff[i]);++i;}HString hStr;ret = InitString(&hStr);CHECK_VALUE(ret != RET_OK, ret);ret = StrAssign(buff, &hStr);CHECK_VALUE(ret != RET_OK, ret);int retVal;i = 0;do {ret = StrCompare(&hStr, &(idxList.item[i].key), &retVal);CHECK_VALUE(ret != RET_OK, ret);++i;} while ((retVal != 0) && (i < idxList.last));if (retVal) {printf("Not Found!\n");} else {Link p = idxList.item[i - 1].bookNumList.head->next;while (p) {int j;for (j = 0; (j < bookList.last) && (p->data != bookList.item[j].bookNum); ++j);if (j < bookList.last) {printf("%s\n", bookList.item[j].bookName);}p = p->next;}}/* Step 3 */for (int i = 0; i < normalIdx.last; ++i) {free(normalIdx.item[i]);}for (int i = 0; i < idxList.last; ++i) {ret = DestroyList(&(idxList.item[i].bookNumList));CHECK_VALUE(ret != RET_OK, ret);ret = ClearString(&(idxList.item[i].key));CHECK_VALUE(ret != RET_OK, ret);}return 0;
}

3. 运行示例

生成文件 idxList.txt 

9
algorithms
1
34 
analysis
3
34 50 67 
computer
2
5 34 
data
3
5 10 23 
design
1
34 
fundamentals
1
23 
introduction
2
10 50 
numerical
2
50 67 
structures
3
5 10 23 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/148402.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

文件编码格式

一、问题场景 笔者在写controller层出现了一些小问题&#xff1a;测试controller层的一些请求的时候&#xff0c;后端控制台打印的是乱码&#xff0c;网上找了很多说改UTF-8的&#xff0c;但是我去设置里面全部都改为UTF-8了&#xff0c;结果仍然无济于事&#xff0c;甚至还把…

学过的汇编指令整合

1.数据搬移指令 <opcode>{<cond>}{s} <Rd>, <shifter_operand> 解释&#xff1a; <opcode>&#xff1a;指令码 {<cond>}&#xff1a;条件码 {s}&#xff1a;状态位&#xff0c;如果在指令后面加上s&#xff0c;则运算的结果会影响CPSR的条…

2023-09-28 monetdb-databae的概念和作用-分析

摘要: 每个数据库对于db,schema以及user,role都有一套自己的设计, 不同数据库间对于相同名字的东西例如database和schema可以说南辕北辙, 例如mysql中schema其实是database的同义词. 本文分析monetdb的database的概念和作用 database的概念和作用: 和mysql的database完全不同…

Windows11安装MySQL8.1

安装过程中遇到任何问题均可以参考(这个博客只是单纯升级个版本和简化流程) Windows安装MySQL8教程-CSDN博客 到官网下载mysql8数据库软件 MySQL :: Download MySQL Community Server 下载完后,解压到你需要安装的文件夹 其中的配置文件内容了如下 [mysqld]# 设置3306端口po…

云原生微服务 第六章 Spring Cloud Netflix Eureka集成OpenFeign组件,实现微服务的远程调用、负载均衡

系列文章目录 第一章 Java线程池技术应用 第二章 CountDownLatch和Semaphone的应用 第三章 Spring Cloud 简介 第四章 Spring Cloud Netflix 之 Eureka 第五章 Spring Cloud Netflix 之 Ribbon 第六章 Spring Cloud 之 OpenFeign 文章目录 系列文章目录前言1、OpenFeign的实现…

STM32CubeMX学习笔记-USB接口使用(HID按键)

STM32CubeMX学习笔记-USB接口使用&#xff08;HID按键&#xff09; 一、USB简介1.1 USB HID简介 二、新建工程1. 打开 STM32CubeMX 软件&#xff0c;点击“新建工程”2. 选择 MCU 和封装3. 配置时钟4. 配置调试模式 三、USB3.1 参数配置3.2 引脚配置3.3 配置时钟3.4 USB Device…

Transformer学习-self-attention

这里写自定义目录标题 Self-attentionMulti-head self-attention用self-attention解决其他问题 Self-attention 用Wq、Wk、Wv分别乘输入向量得到q、k、v向量 用每个q向量乘所有的k向量得到对应项的attention&#xff0c;即用每项的query向量去匹配所有的key向量&#xff0c;得…

数字IC前端学习笔记:数字乘法器的优化设计(阵列乘法器)

相关阅读 数字IC前端https://blog.csdn.net/weixin_45791458/category_12173698.html?spm1001.2014.3001.5482 数字信号处理作为微处理器的核心部件&#xff0c;是决定着总体处理器性能的因素之一&#xff0c;而数字乘法器是最常见的一种数字信号处理电路。通常情况下&#…

python二次开发CATIA:为选中元素上色

先打开一个零件文档&#xff0c;然后用鼠标选中元素&#xff0c;再运行如下python程序&#xff1a; import win32com.client import pywintypes # 导入pywintypes模块 import random # 启动CATIA应用 catia win32com.client.Dispatch(CATIA.Application) catia.visible1try:…

from PIL import Image,文字成图,ImageFont import jieba分词,input优雅python绘制图片

开始的代码 import os from PIL import Image, ImageDraw, ImageFont import jiebadef generate_image_with_white_bg(text, font_path, output_path):# 设置图片大小和背景颜色image_width 800image_height 600bg_color (255, 255, 255) # 白色# 创建图片对象image Imag…

WOL唤醒配置(以太网、PHY、MAC)

目录 wol 以太网 MAC PHY RMII 通信配置 总结 wol Wake-on-LAN简称WOL&#xff0c;WOL&#xff08;网络唤醒&#xff09; 是一种标准网络协议&#xff0c;它的功效在于让已经进入休眠状态或关机状态的计算机&#xff0c;透过局域网&#xff08;多半为以太网&#xff…

java图书管理系统

一、 引言 图书管理系统是一个用于图书馆或书店管理图书信息、借阅记录和读者信息的应用程序。本系统使用Java Swing框架进行开发&#xff0c;提供直观的用户界面&#xff0c;方便图书馆管理员或书店工作人员对图书信息进行管理。以下是系统的设计、功能和实现的详细报告。 二…

29 drf-Vue个人向总结-2

文章目录 drf项目总结2重写create自定义验证类获取个性化内容 与 lookup_field 的用处重写get_queryset&#xff0c;get_serializer_class类docs帮助文档支付宝支付原理&#xff08;微信同原理&#xff09;使用流程创建公钥私钥使用的理论介绍使用的代码介绍支付宝与Drf的联合使…

python中实现定时任务的几种方案

目录 while True: sleep()Timeloop库threading.Timersched模块schedule模块APScheduler框架Celery框架数据流工具Apache Airflow概述Airflow 核心概念Airflow 的架构 总结以下几种方案实现定时任务&#xff0c;可根据不同需求去使用不同方案。 while True: sleep() 利用whil…

Pytorch目标分类深度学习自定义数据集训练

目录 一&#xff0c;Pytorch简介&#xff1b; 二&#xff0c;环境配置&#xff1b; 三&#xff0c;自定义数据集&#xff1b; 四&#xff0c;模型训练&#xff1b; 五&#xff0c;模型验证&#xff1b; 一&#xff0c;Pytorch简介&#xff1b; PyTorch是一个开源的Python机…

【4】c++设计模式——>UML表示类之间的聚合关系

聚合关系表示整体与部分的关系&#xff0c;在聚合关系中&#xff0c;成员对象时整体的一部分&#xff0c;但是成员对象可以脱离整体对象独立存在&#xff0c;当整体被析构销毁的时候&#xff0c;组成整体的这些子对象是不会被销毁的&#xff0c;是可以继续存活&#xff0c;并在…

Hono——一个小型,简单且超快的Edges Web框架

Hono - [炎]在日语中的意思是火焰&#x1f525; - 是一个小型&#xff0c;简单且超快的Edges Web框架。它适用于任何JavaScript运行时&#xff1a;Cloudflare Workers&#xff0c;Fastly ComputeEdge&#xff0c;Deno&#xff0c;Bun&#xff0c;Vercel&#xff0c;Netlify&…

机器学习 不均衡数据采样方法:imblearn 库的使用

✅作者简介&#xff1a;人工智能专业本科在读&#xff0c;喜欢计算机与编程&#xff0c;写博客记录自己的学习历程。 &#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&…

CSS3与HTML5

box-sizing content-box&#xff1a;默认&#xff0c;宽高包不含边框和内边距 border-box&#xff1a;也叫怪异盒子&#xff0c;宽高包含边框和内边距 动画&#xff1a;移动translate&#xff0c;旋转、transform等等 走马灯&#xff1a;利用动画实现animation&#xff1a;from…

【C++进阶(七)】仿函数深度剖析模板进阶讲解

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习C   &#x1f51d;&#x1f51d; 模板进阶 1. 前言2. 仿函数的概念3. 仿函数的实…