第 4 章 串(串的块链存储实现)

news2024/9/27 15:27:09

1. 背景说明

该实现和链表的实现极为相似,只是将链接的内存拆分为具体的大小的块。

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) lString.h

/* 串的块链存储实现头文件 */

#ifndef LSTRING_H
#define LSTRING_H

#include "status.h"

#define CHUNK_SIZE 4
#define BLANK '#'

typedef struct Chunk {
	char str[CHUNK_SIZE];
	struct Chunk *next;
} Chunk;

typedef struct {
	Chunk *head, *tail;
	int curLen;				/* 字符个数 */
} LString;

/* 初始化(产生空串)字符串 T */
Status InitString(LString *T);

/* 生成一个其值等于 chars 的串 T (要求 chars 中不包含填补空余的字符)
   成功返回 OK,否则返回 ERROR */
Status StrAssign(const char *chars, LString *T);

/* 初始条件: 串 S 存在
   操作结果: 由串 S 复制得串 T(连填补空余的字符一块拷贝) */
Status StrCopy(const LString *S, LString *T);

/* 初始条件:串 S 存在
   操作结果:若 S 为空串,则返回 TRUE,否则返回 FALSE */
Bollean StrEmpty(const LString *S);

/* 若 S > T,则返回值 > 0;若 S = T,则返回值 = 0;若 S < T, 则返回值 < 0 */
int StrCompare(const LString *S, const LString *T);

/* 返回 S 的元素个数,称为串的长度 */
int StrLength(const LString *S);

/* 初始条件: 串 S 存在
   操作结果: 将 S 清为空串 */
Status ClearString(LString *S);

/* 用 T 返回由 S1 和 S2 联接而成的新串 */
Status Concat(const LString *S1, const LString *S2, LString *T);

/* 用 Sub 返回串 S 的第 pos 个字符起长度为 len 的子串
   其中, 1≤ pos ≤ StrLength(S) 且 0 ≤ len ≤ StrLength(S) - pos + 1 */
Status SubString(const LString *S, int pos, int len, LString *Sub);

/* T 为非空串。若主串 S 中第 pos 个字符之后存在与 T 相等的子串
   则返回第一个这样的子串在 S 中的位置,否则返回 0 */
int Index(const LString *S, const LString *T, int pos);

/* 压缩串(清除块中不必要的填补空余的字符) */
Status Zip(LString *S);

/* 1 ≤ pos ≤ StrLength(S) + 1。在串 S 的第 pos 个字符之前插入串 T */
Status StrInsert(const LString *T, int pos, LString *S);

/* 从串 S 中删除第 pos 个字符起长度为 len 的子串 */
Status StrDelete(int pos, int len, LString *S);

/* 初始条件: 串 S, T 和 V 存在,T 是非空串(此函数与串的存储结构无关)
   操作结果: 用 V 替换主串 S 中出现的所有与 T 相等的不重叠的子串 */
Status Replace(const LString *T, const LString *V, LString *S);

/*  输出字符串 T */
void StrPrint(const LString *T);

#endif // !LSTRING_H

3) lString.c

/* 串的块链存储实现源文件 */

#include "lString.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/* 初始化(产生空串)字符串 T */
Status InitString(LString *T)
{
	CHECK_VALUE(!T, ERR_NULL_PTR);
	T->curLen = 0;
	T->head = NULL;
	T->tail = NULL;

	return RET_OK;
}

/* 生成一个其值等于 chars 的串 T (要求 chars 中不包含填补空余的字符)
   成功返回 OK,否则返回 ERROR */
Status StrAssign(const char *chars, LString *T)
{
	CHECK_VALUE(!chars || !T, ERR_NULL_PTR);
	int length = (int)strlen(chars);
	CHECK_VALUE((length == 0) || strchr(chars, BLANK), ERR_PARA);
	T->curLen = length;
	int nodes = length / CHUNK_SIZE;
	if (length % CHUNK_SIZE) {
		nodes += 1;
	}

	Chunk *tail = NULL, *newNode = NULL;
	for (int i = 0; i < nodes; ++i) {
		newNode = (Chunk *)malloc(sizeof(Chunk));
		CHECK_VALUE(!newNode, ERR_NULL_PTR);
		if (i == 0) {
			T->head = tail = newNode;
		} else {
			tail->next = newNode;
			tail = newNode;
		}

		int j;
		for (j = 0; (j < CHUNK_SIZE) && (*chars); ++j) {
			*(tail->str + j) = *chars++;
		}

		if (!(*chars)) {
			T->tail = tail;
			tail->next = NULL;
			while (j < CHUNK_SIZE) {
				*(tail->str + j++) = BLANK;
			}
		}
	}

	return RET_OK;
}

/* 初始条件: 串 S 存在
   操作结果: 由串 S 复制得串 T(连填补空余的字符一块拷贝) */
Status StrCopy(const LString *S, LString *T)
{
	CHECK_VALUE(!S || !T, ERR_NULL_PTR);
	Chunk *sHead = S->head, *newNode = NULL;
	T->head = NULL;
	while (sHead) {
		newNode = (Chunk *)malloc(sizeof(Chunk));
		CHECK_VALUE(!newNode, ERR_MEMORY_ALLOCATE);
		newNode->next = NULL;
		(void)memcpy_s(newNode, sizeof(Chunk), sHead, sizeof(Chunk));
		if (T->head == NULL) {
			T->head = T->tail = newNode;
		} else {
			T->tail->next = newNode;
			T->tail = newNode;
		}

		sHead = sHead->next;
	}

	T->curLen = S->curLen;

	return RET_OK;
}

/* 初始条件:串 S 存在
   操作结果:若 S 为空串,则返回 TRUE,否则返回 FALSE */
Bollean StrEmpty(const LString *S)
{
	CHECK_VALUE(!S, ERR_NULL_PTR);

	return (S->curLen == 0) ? TRUE : FALSE;
}

static void GetNextCharPos(Chunk **node, int *order)
{
	++(*order);
	if (*order == CHUNK_SIZE) {
		*node = (*node)->next;
		*order = 0;
	}
}

static void GetNextLegalCharPos(Chunk **node, int *order)
{
	while (*((*node)->str + *order) == BLANK) {
		GetNextCharPos(node, order);
	}
}

/* 若 S > T,则返回值 > 0;若 S = T,则返回值 = 0;若 S < T, 则返回值 < 0 */
int StrCompare(const LString *S, const LString *T)
{
	CHECK_VALUE(!S || !T, ERR_NULL_PTR);
	Chunk *ps = S->head, *pt = T->head;
	for (int i = 0, js = 0, jt = 0; (i < S->curLen) && (i < T->curLen); ++i) {
		GetNextLegalCharPos(&ps, &js);
		GetNextLegalCharPos(&pt, &jt);
		if (*(ps->str + js) != *(pt->str + jt)) {
			return *(ps->str + js) - *(pt->str + jt);
		}

		GetNextCharPos(&ps, &js);
		GetNextCharPos(&pt, &jt);
	}

	return S->curLen - T->curLen;
}

/* 返回 S 的元素个数,称为串的长度 */
int StrLength(const LString *S)
{
	CHECK_VALUE(!S, ERR_NULL_PTR);

	return S->curLen;
}

/* 初始条件: 串 S 存在
   操作结果: 将 S 清为空串 */
Status ClearString(LString *S)
{
	CHECK_VALUE(!S, ERR_NULL_PTR);
	Chunk *p = S->head, *q = NULL;
	while (p) {
		q = p->next;
		free(p);
		p = q;
	}

	S->head = S->tail = NULL;
	S->curLen = 0;

	return RET_OK;
}

/* 用 T 返回由 S1 和 S2 联接而成的新串 */
Status Concat(const LString *S1, const LString *S2, LString *T)
{
	CHECK_VALUE(!S1 || !S2 || !T, ERR_NULL_PTR);
	LString str1, str2;
	InitString(&str1);
	InitString(&str2);
	StrCopy(S1, &str1);
	StrCopy(S2, &str2);
	T->head = str1.head;
	str1.tail->next = str2.head;
	T->tail = str2.tail;
	T->curLen = str1.curLen + str2.curLen;

	return RET_OK;
}

/* 用 Sub 返回串 S 的第 pos 个字符起长度为 len 的子串
   其中, 1≤ pos ≤ StrLength(S) 且 0 ≤ len ≤ StrLength(S) - pos + 1 */
Status SubString(const LString *S, int pos, int len, LString *Sub)
{
	CHECK_VALUE(!S || !Sub, ERR_NULL_PTR);
	CHECK_VALUE((pos < 1) || (pos > S->curLen) || (len < 0) || (len > (S->curLen - pos + 1)), ERR_PARA);
	int subLength = len / CHUNK_SIZE;
	if (len % CHUNK_SIZE) {
		subLength += 1;
	}

	Chunk *newNode = (Chunk *)malloc(sizeof(Chunk));
	Sub->head = newNode;
	Chunk *tail = Sub->head;
	for (int i = 0; i < subLength - 1; ++i) {
		newNode = (Chunk *)malloc(sizeof(Chunk));
		tail->next = newNode;
		tail = newNode;
	}

	tail->next = NULL;
	Sub->tail = tail;
	Sub->curLen = len;
	int lastPos = len % CHUNK_SIZE;
	if (lastPos) {
		for (int i = lastPos; i < CHUNK_SIZE; ++i) {
			*(newNode->str + i) = BLANK;
		}
	}

	Chunk *subHead = Sub->head, *sHead = S->head;
	int subPos = 0, count = 0;
	Bollean isEnd = FALSE;
	while (!isEnd) {
		for (int i = 0; i < CHUNK_SIZE; ++i) {
			if (*(sHead->str + i) == BLANK) {
				continue;
			}

			++count;
			if ((count >= pos) && (count <= pos + len - 1)) {
				if (subPos == CHUNK_SIZE) {
					subHead = subHead->next;
					subPos = 0;
				}

				*(subHead->str + subPos) = *(sHead->str + i);
				++subPos;
				if (count == pos + len - 1) {
					isEnd = TRUE;
					break;
				}
			}
		}

		sHead = sHead->next;
	}

	return RET_OK;
}

/* T 为非空串。若主串 S 中第 pos 个字符之后存在与 T 相等的子串
   则返回第一个这样的子串在 S 中的位置,否则返回 0 */
int Index(const LString *S, const LString *T, int pos)
{
	CHECK_VALUE(!S || !T, ERR_NULL_PTR);
	int maxRange = StrLength(S) - StrLength(T) + 1;
	CHECK_VALUE((pos < 1) || (pos > maxRange), 0);
	LString sub;
	InitString(&sub);
	while (pos <= maxRange) {
		SubString(S, pos, StrLength(T), &sub);
		if (StrCompare(T, &sub) == 0) {
			return pos;
		}

		++pos;
	}

	return 0;
}


/* 压缩串(清除块中不必要的填补空余的字符) */
Status Zip(LString *S)
{
	CHECK_VALUE(!S, ERR_NULL_PTR);
	char *newStr = (char *)malloc(sizeof(char) * (unsigned int)(S->curLen + 1));
	CHECK_VALUE(!newStr, ERR_NULL_PTR);
	Chunk *sHead = S->head;
	int count = 0;
	while (sHead) {
		for (int i = 0; i < CHUNK_SIZE; ++i) {
			if (*(sHead->str + i) != BLANK) {
				*(newStr + count) = *(sHead->str + i);
				++count;
			}
		}

		sHead = sHead->next;
	}

	*(newStr + count) = '\0';
	ClearString(S);
	StrAssign(newStr, S);

	return RET_OK;
}

/* 1 ≤ pos ≤ StrLength(S) + 1。在串 S 的第 pos 个字符之前插入串 T */
Status StrInsert(const LString *T, int pos, LString *S)
{
	CHECK_VALUE(!T || !S, ERR_MEMORY_ALLOCATE);
	CHECK_VALUE((pos < 1) || (pos > StrLength(S) + 1), ERR_PARA);
	LString t;
	StrCopy(T, &t);
	Zip(S);
	int moveBlock = (pos - 1) / CHUNK_SIZE;
	int insertPos = (pos - 1) % CHUNK_SIZE;
	Chunk *sHead = S->head;
	if (pos == 1) {
		t.tail->next = S->head;
		S->head = t.head;
	} else if (insertPos == 0) {
		for (int i = 0; i < moveBlock - 1; ++i) {
			sHead = sHead->next;
		}

		Chunk *insertNext = sHead->next;
		sHead->next = t.head;
		t.tail->next = insertNext;
		if (insertNext == NULL) {
			S->tail = t.tail;
		}
	} else {
		for (int i = 0; i < moveBlock; ++i) {
			sHead = sHead->next;
		}

		Chunk *newBlock = (Chunk *)malloc(sizeof(Chunk));
		CHECK_VALUE(!newBlock, ERR_NULL_PTR);
		for (int i = 0; i < insertPos; ++i) {
			*(newBlock->str + i) = BLANK;
		}

		for (int i = insertPos; i < CHUNK_SIZE; ++i) {
			*(newBlock->str + i) = *(sHead->str + i);
			*(sHead->str + i) = BLANK;
		}

		newBlock->next = sHead->next;
		sHead->next = t.head;
		t.tail->next = newBlock;
	}

	S->curLen += t.curLen;
	Zip(S);

	return RET_OK;
}

/* 从串 S 中删除第 pos 个字符起长度为 len 的子串 */
Status StrDelete(int pos, int len, LString *S)
{
	CHECK_VALUE(!S, ERR_NULL_PTR);
	CHECK_VALUE((pos < 1) || (pos > S->curLen - len + 1) || (len < 0), ERR_PARA);
	int count = 0;
	int currOrder = 0;
	Chunk *sHead = S->head;
	while (count < pos - 1) {
		GetNextLegalCharPos(&sHead, &currOrder);
		++count;
		GetNextCharPos(&sHead, &currOrder);
	}

	++count;
	if (*(sHead->str + currOrder) == BLANK) {
		GetNextLegalCharPos(&sHead, &currOrder);
	}

	while (count < pos + len) {
		GetNextLegalCharPos(&sHead, &currOrder);
		*(sHead->str + currOrder) = BLANK;
		++count;
		GetNextCharPos(&sHead, &currOrder);
	}

	S->curLen -= len;

	return RET_OK;
}

/* 初始条件: 串 S, T 和 V 存在,T 是非空串(此函数与串的存储结构无关)
   操作结果: 用 V 替换主串 S 中出现的所有与 T 相等的不重叠的子串 */
Status Replace(const LString *T, const LString *V, LString *S)
{
	CHECK_VALUE(!T || !V || !S, ERR_NULL_PTR);
	CHECK_VALUE(StrEmpty(T), ERR_PARA);
	int pos = 1;
	do {
		pos = Index(S, T, pos);
		if (pos) {
			StrDelete(pos, StrLength(T), S);
			StrInsert(V, pos, S);
			pos += StrLength(V);
		}
	} while (pos);

	return RET_OK;
}

/*  输出字符串 T */
void StrPrint(const LString *T)
{
	int count = 0;
	Chunk *tHead = T->head;
	while (count < T->curLen) {
		for (int i = 0; i < CHUNK_SIZE; ++i) {
			if (*(tHead->str + i) != BLANK) {
				printf("%c", *(tHead->str + i));
				++count;
			}
		}

		tHead = tHead->next;
	}
}

4) main.c

/* 入口程序源文件 */

#include "lString.h"
#include <stdio.h>

void ShowStr(const LString *S, const char *stringName);

int main(void)
{
	LString t1, t2, t3, t4;
	InitString(&t1);
	InitString(&t2);
	InitString(&t3);
	InitString(&t4);
	printf("After initialize the string t1, the string t1 is %s,"
		"the length of string t1 is %d\n", (StrEmpty(&t1) == TRUE) ? "empty" : "not empty",
		StrLength(&t1));
	char *s1 = "ABCDEFGHI", *s2 = "12345", *s3 = "", *s4 = "asd#tr", *s5 = "ABCD";
	Status ret = StrAssign(s3, &t1);
	if (ret == RET_OK) {
		ShowStr(&t1, "t1");
	}

	ret = StrAssign(s4, &t1);
	if (ret == RET_OK) {
		ShowStr(&t1, "t1");
	}

	ret = StrAssign(s1, &t1);
	if (ret == RET_OK) {
		ShowStr(&t1, "t1");
	}

	printf("After assign s1 to the string t1, the string t1 is %s,"
		"the length of string t1 is %d\n", (StrEmpty(&t1) == TRUE) ? "empty" : "not empty",
		StrLength(&t1));
	ret = StrAssign(s2, &t2);
	if (ret == RET_OK) {
		ShowStr(&t2, "t2");
	}

	StrCopy(&t1, &t3);
	ShowStr(&t3, "t3");
	ret = StrAssign(s5, &t4);
	if (ret == RET_OK) {
		ShowStr(&t4, "t4");
	}

	Replace(&t4, &t2, &t3);
	ShowStr(&t3, "t3");
	ClearString(&t1);
	printf("After clear string t1, the string t1 is %s,"
		"the length of string t1 is %d\n", (StrEmpty(&t1) == TRUE) ? "empty" : "not empty",
		StrLength(&t1));
	Concat(&t2, &t3, &t1);
	ShowStr(&t1, "t1");
	Zip(&t1);
	ShowStr(&t1, "t1");
	int pos = Index(&t1, &t3, 1);
	printf("pos = %d\n", pos);
	printf("To insert the string t2 before the posTh character of the string t1, enter pos: ");
	scanf_s("%d", &pos);
	StrInsert(&t2, pos, &t1);
	ShowStr(&t1, "t1");
	int len;
	printf("Please input the position and length of the subString of t1: ");
	scanf_s("%d%d", &pos, &len);
	ClearString(&t2);
	SubString(&t1, pos, len, &t2);
	ShowStr(&t2, "t2");
	printf("StrCompare(&t1, &t2) = %d\n", StrCompare(&t1, &t2));
	printf("Please input the position and length of the string t1 to be delete: ");
	scanf_s("%d%d", &pos, &len);
	StrDelete(pos, len, &t1);
	ShowStr(&t1, "t1");

	t1.head->str[0] = BLANK;
	t1.curLen--;
	printf("t1.head->str[0] = %c\n", t1.head->str[0]);
	ShowStr(&t1, "t1");
	Zip(&t1);
	printf("t1.head->str[0] = %c\n", t1.head->str[0]);
	ShowStr(&t1, "t1");

	ClearString(&t1);
	ClearString(&t2);
	ClearString(&t3);
	ClearString(&t4);

	return 0;
}

void ShowStr(const LString *S, const char *stringName)
{
	printf("The string %s is: ", stringName);
	StrPrint(S);
	printf("\n");
}

3. 运行示例

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

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

相关文章

Neoj4 cypher脚本基本操作

输入网址后&#xff0c;进入默认数据库 操作一&#xff0c;创建一个节点 CREATE (node:Label {property: value})#解释 CREATE (变量名:类名 {节点属性名: 属性值})#举例 CREATE (node1:Person {name: xiao ming}) CREATE (node2:Person {name: xiao hong}) 操作二&#xff0…

行业追踪,2023-09-20

自动复盘 2023-09-20 凡所有相&#xff0c;皆是虚妄。若见诸相非相&#xff0c;即见如来。 k 线图是最好的老师&#xff0c;每天持续发布板块的rps排名&#xff0c;追踪板块&#xff0c;板块来开仓&#xff0c;板块去清仓&#xff0c;丢弃自以为是的想法&#xff0c;板块去留让…

@Valid注解的作用及@Valid注解与@Validated的区别

1.Valid注解 导入依赖 <dependency><groupId>javax.validation</groupId><artifactId>validation-api</artifactId></dependency><dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate…

【Java 基础篇】Java后台线程和守护线程详解

在Java多线程编程中&#xff0c;有两种特殊类型的线程&#xff1a;后台线程&#xff08;Daemon Thread&#xff09;和守护线程&#xff08;Daemon Thread&#xff09;。这两种线程在一些特定的场景下非常有用&#xff0c;但也需要谨慎使用。本文将详细介绍后台线程和守护线程的…

交换机端口汇聚详解

交换机端口汇聚是一种网络设计技术&#xff0c;用于将多个物理端口汇集成一个逻辑链路&#xff0c;以提供更高的带宽和冗余。通过端口汇聚&#xff0c;可以增加网络的吞吐量&#xff0c;并提高链路的可靠性和可用性。以下是关于交换机端口汇聚的详细介绍&#xff1a; 工作原理&…

【测试开发】用例篇 · 熟悉黑盒测试用例设计方法(1)等价类划分法、边界值法、判定表法

【测试开发】用例篇&#xff08;1&#xff09; 文章目录 【测试开发】用例篇&#xff08;1&#xff09;1. 测试用例的基本要素2. 测试用例的设计方法2.1 基于需求的设计方法&#xff08;设计测试点&#xff09;2.2 等价类划分法&#xff08;测试点>测试用例&#xff09;2.2.…

解锁 zkSync Era:开创全新的 Layer 2 扩展时代

作者: stellafootprint.network 数据来源: zkSync Dashboard 在解决以太坊扩展性问题方面&#xff0c;Layer 2 解决方案备受关注。这些解决方案旨在通过引入 Rollups, State Channels 或 Nested Blockchains 等技术来克服 Layer 1 的局限性。在 Layer 2 扩展领域&#xff0c;…

ROS Melodic安装

参考链接 链接: HinGwenWoong大佬 链接: 天月3大佬 本文用两篇文章互为参考&#xff0c;解决了两位大佬的文章在安装时产生的问题。 添加国内源 sudo sh -c . /etc/lsb-release && echo "deb http://mirrors.tuna.tsinghua.edu.cn/ros/ubuntu/ lsb_release -cs…

UWB定位模块

UWB定位模组是华星智控自研的小尺寸高集成度模组&#xff0c;模组长宽厚为30.1513.955.62毫米&#xff0c;天线采用IPEX接口分体式设计&#xff0c;方便集成于您的产品中&#xff0c;产品采用本安设计&#xff0c;可以用于煤矿等井下场景&#xff0c;通信距离>100米&#xf…

子网掩码的作用

1.子网掩码的作用 子网掩码是用来给ip划分网络位和主机位的。 子网掩码是为了给ip确定谁是网络地址、谁是主机地址的。子网掩码的二进制位是1的对应的是网络地址&#xff0c;子网掩码的二进制位是0的对应的是主机地址。

[PowerQuery] PowerAutoMate 刷新PowerBI 数据

通过PowerBI Automate 进行PowerBI 数据刷新之前,需要有Power Automate 授权或者Power Automate 试用账户,可以通过如下的地址进行申请注册。 https://flow.microsoft.com/zh-cn/ 完成Power Automate 登录之后,选中计划的云端流后创建,图为创建计划的云端流的操作步骤。 …

小程序随机生成文字卡片文案海报,带分享保存

概述 文字随机生成、更换头像、生成卡片、保存卡片、分享卡片 详细 文字随机生成、更换头像、生成卡片、保存卡片、分享卡片 数据是在data.js中 随机文案获取&#xff1a; demo直接在微信开发者工具可以运行 index.xml 代码 <view class"index"> <view …

02-HTML常用标签

02-HTML常用标签 2.1 标签的构成 标签由<、>、/、英文单词或字母组成。并且把标签中<>包括起来的英文单词或字母称为标签名常见标签由两部分组成&#xff0c;我们称之为&#xff1a;双标签。前部分叫开始标签&#xff0c;后部分叫结束标签&#xff0c;两部分之间…

C++ - AVL 树 介绍 和 实现 (上篇)

前言 之前我介绍了 二叉搜索树&#xff0c;可看一下博客&#xff1a;C - 搜索二叉树_chihiro1122的博客-CSDN博客 二叉搜索树的效率可以达到 O(log n) 。这个复杂度的算法的效率是非常恐怖的&#xff0c;2 的 30 次方大概是 10亿左右。也就是说 如果用暴力查找 需要找 10 亿次&…

HTTPS加密过程详解

目录 一、HTTPS是什么 1.1 运营商劫持 1.2 加密是什么 二、HTTPS的工作过程 2.1 对称加密 2.2 非对称加密 2.3 引入证书 一、HTTPS是什么 HTTPS 也是一个应用层协议。是在 HTTP 协议的基础上引入了一个加密层。 HTTP 协议内容都是按照文本的方式明文传输的。这就导致在传输过程…

Linux——vi编辑器

目录 一、基本简介 二、命令模式下的常用按键 1、光标跳转按键 2、复制、粘贴、删除 三、编辑模式 四、末行模式 1、查找关键字并替换 2、保存退出 3、其他操作 五、模式切换 一、基本简介 1、最早可追随到1991年&#xff0c;全称为“Vi IMproved” 2、模式 ——命…

【使用Cpolar将Tomcat网页传输到公共互联网上】

文章目录 1.前言2.本地Tomcat网页搭建2.1 Tomcat安装2.2 配置环境变量2.3 环境配置2.4 Tomcat运行测试2.5 Cpolar安装和注册 3.本地网页发布3.1.Cpolar云端设置3.2 Cpolar本地设置 4.公网访问测试5.结语 1.前言 Tomcat作为一个轻量级的服务器&#xff0c;不仅名字很有趣&#…

护网行动,最全攻略来啦!!!

随着网络技术的不断发展&#xff0c;网络领域被发现的安全威胁越来越多。 病毒入侵、数据窃取、网络攻击等安全事件时常发生&#xff0c;网络已然成为各国无声较量的重要战略空间。 为应对网络安全威胁&#xff0c;严守网络安全底线&#xff0c;公安部自2016年开始组织多家机构…

基于微信小程序的高校宿舍信息管理系统设计与实现(源码+lw+部署文档+讲解等)

前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f447;&#x1f3fb;…

Centos/Ubuntu安装redis

一、Centos安装redis 首先查看gcc是否安装 gcc -v 出现版本号就说明可以直接安装redis&#xff0c;没有的话需要安装gcc这是 下载路径&#xff1a; 【免费】gccCentos.rar资源-CSDN文库 rpm -ivh *.rpm --nodeps --force #安装 服务器有网的话可以直接下载redis包 wget https…