【Godot4.2】GodotXML插件 - 解析和生成XML

概述

近期在研究Godot的XML和SVG解析,Godot提供了XMLParser类型,但本身只提供了低级的XML解析接口和功能,想要完成完整的XML文档解析和存储可能还需要在其基础上编写类或功能,就像我在昨天(2024年7月20日)基于XMLParser编写了一个简易的SVG文件解析器SVGParser。

但是我早就知道存在GodotXML这样的解析器。加上群友推荐,于是上手试用了一下。和以往一样,看源码里一堆英文注释就手痒,就机翻之,感觉代码布局不合理就稍加改动,并自己加一些注释。

本文主要简介GodotXML项目及其核心文件和核心类的方法,并贴出了汉化后的源代码内容。希望大家一起来学习和使用。

GodotXML简介

  • GodotXML是一个为Godot4.x引擎提供高级XML支持的插件。支持Godot 4.0-4.2,以及可能的未来版本。
  • 项目代码在Github上开源,项目地址:https://github.com/elenakrittik/GodotXML。

功能

  • 基于GDSCript内置的XMLParser编写
  • 可以解析文件或字符串形式XML数据,并方便转换为GDSCript中的字典形式
  • 美化XML(带换行和Tab缩进,类似于VSCode中的“文档格式化”功能)
  • 可以基于GDSCript对象形式生成XML文档结构并方便存储

下载安装

  • 在Godot4资产库中搜索 “GodotXML” 插件安装
  • 或者在Github项目主页下载ZIP压缩包到本地,将 addons/ 文件夹复制到项目的根目录。

下载下来后核心只有3个.gd文件,说是插件其实本质就是1个静态函数库+2个类。
image.png

源码翻译+重排

XML

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qnBr0RJ5-1721699899307)(https://i-blog.csdnimg.cn/direct/e9d1943b34444ef49256d5d2ddaca551.png)]

  • xml.gd内部是一个class_nameXML的静态函数库
  • 使用XML.parse_file()可以解析XML文件为XMLDocument实例
  • 使用XML.parse_string()可以解析XML字符串为XMLDocument实例
  • 使用XML.parse_buffer()可以解析PackedByteArray类型的XML内容为XMLDocument实例

源代码:

# ==================================================================
# 名称:XML
# 描述:GodotXML - Godot4的高级XML支持。此类允许从文件解析XML数据并将其转储到各种源。
# 原作者:elenakrittik(https://github.com/elenakrittik)
# 翻译+重排:巽星石 最后修改:202472115:56:39
# ==================================================================
class_name XML extends RefCounted
# =============================== 表层解析函数(直接调用) ===============================
# 将文件内容作为XML解析为 XMLDocument 实例
# 指定路径处的文件必须可读,文件内容必须是语法有效的XML文档。
static func parse_file(path: String) -> XMLDocument:
	var file = FileAccess.open(path, FileAccess.READ)
	var xml: PackedByteArray = file.get_as_text().to_utf8_buffer()
	file = null
	return XML._parse(xml)

# 将字符串作为XML解析为 XMLDocument 实例
# 字符串内容必须是语法有效的XML文档
static func parse_str(xml: String) -> XMLDocument:
	return XML._parse(xml.to_utf8_buffer())

# 将字 PackedByteArray 作为XML解析为 XMLDocument 实例
# PackedByteArray 内容必须是语法有效的XML文档。
static func parse_buffer(xml: PackedByteArray) -> XMLDocument:
	return XML._parse(xml)

# =============================== 核心解析函数(底层、被调用) ===============================
# 被parse_file()parse_str()parse_buffer()调用
static func _parse(xml: PackedByteArray) -> XMLDocument:
	xml = _cleanup_double_blankets(xml)  # 见函数体中的注释

	var doc := XMLDocument.new()
	var queue: Array[XMLNode] = []  # 未关闭的标签队列

	var parser := XMLParser.new()
	parser.open_buffer(xml)

	while parser.read() != ERR_FILE_EOF:
		var node: XMLNode = _make_node(queue, parser)

		# 如果节点类型为NODE_TEXT,则没有节点,因此我们跳过
		if node == null:
			continue

		# 如果我们刚刚开始,我们将第一个节点设置为根节点并初始化队列
		if len(queue) == 0:
			doc.root = node
			queue.append(node)
		else:
			var node_type := parser.get_node_type()

			# 下面,`queue.back().childs.append(…)`表示:
			# - 获取最后一个节点
			# - 因为我们在那个未闭合的节点内,所以我们得到的所有非闭合节点都是它的子节点
			# - 因此,我们访问.childs并将我们的非闭合节点附加到它们上
			
			# 希望这能说明一切
			if node.standalone:
				queue.back().children.append(node)

			# 这里一样
			elif node_type == XMLParser.NODE_ELEMENT_END:
				var last := queue.pop_back()  # 获取删除最后一个未关闭的节点

				# 如果我们有一个关闭节点,但它的名称与打开节点的名称不同,这是一个错误
				if node.name != last.name:
					push_error(
						"Invalid closing tag: started with %s but ended with %s. Ignoring (output may be incorrect)." % [last.name, node.name]
					)
					# 我们没有在这里打断,而是继续,因为无效的名字往往只是一个拼写错误
					continue

				# we just closed a node, so if the queue is empty we stop parsing (effectively ignoring
				# anything past the first root). this is done to prevent latter roots overwriting former
				# ones in case when there's more than one root (invalid per standard, but still used in
				# some documents). we do not natively support multiple roots (and will not, please do not
				# open PRs for that), but if the user really needs to, it is trivial to wrap the input with
				# another "housing" node.
				
				# 我们刚刚关闭了一个节点,因此如果队列为空,我们将停止解析(实际上忽略了
				# 任何超过第一根的东西)。这样做是为了防止后根覆盖前根
				# 如果有多个根(根据标准无效,但仍在使用
				# 一些文件)。我们本身不支持多个根(也不会,请不要
				# 为此打开PR),但如果用户真的需要,用以下内容包装输入是很简单的
				# 另一个“住房”节点。
				if queue.is_empty():
					break

			# opening node
			else:
				queue.back().children.append(node)
				queue.append(node)  # 移动到节点体内

	# 如果解析已结束,但仍有未关闭的节点,我们会报告它
	if not queue.is_empty():
		queue.reverse()
		var names: Array[String] = []

		for node in queue:
			names.append(node.name)

		push_error("The following nodes were not closed: %s" % ", ".join(names))

	return doc

# =============================== 转换函数 ===============================
# 将 XMLDocument 实例转为纯文本存储到指定文件
static func dump_file(
	path: String,
	document: XMLDocument,
	pretty: bool = false,
	indent_level: int = 0,
	indent_length: int = 2
) -> void:
	return document.root.dump_file(path, pretty, indent_level, indent_length)


# 将 XMLDocument 实例转为 PackedByteArray形式
static func dump_buffer(
	document: XMLDocument,
	pretty: bool = false,
	indent_level: int = 0,
	indent_length: int = 2,
) -> PackedByteArray:
	return document.root.dump_buffer(pretty, indent_level, indent_length)


## 将 XMLDocument 实例转为 String 形式
static func dump_str(
	document: XMLDocument,
	pretty: bool = false,
	indent_level: int = 0,
	indent_length: int = 2,
) -> String:
	return document.root.dump_str(pretty, indent_level, indent_length)


# 创建节点
static func _make_node(queue: Array[XMLNode], parser: XMLParser) -> Variant:
	var node_type := parser.get_node_type()

	match node_type:
		XMLParser.NODE_ELEMENT:
			return XML._make_node_element(parser)
		XMLParser.NODE_ELEMENT_END:
			return XML._make_node_element_end(parser)
		XMLParser.NODE_TEXT:
			# 忽略根节点前的空白文本;这样更容易,相信我
			if queue.is_empty():
				return
			XML._attach_node_data(queue.back(), parser)
			return
		XMLParser.NODE_CDATA:
			if queue.is_empty():
				return
			_attach_node_cdata(queue.back(), parser)
			return

	return

# 创建节点元素
static func _make_node_element(parser: XMLParser) -> XMLNode:
	var node := XMLNode.new()

	node.name = parser.get_node_name()
	node.attributes = XML._get_attributes(parser)
	node.content = ""
	node.standalone = parser.is_empty()  # see .is_empty() docs
	node.children = []

	return node

# 创建结束标签
static func _make_node_element_end(parser: XMLParser) -> XMLNode:
	var node := XMLNode.new()

	node.name = parser.get_node_name()
	node.attributes = {}
	node.content = ""
	node.standalone = false  # 独立节点始终为NODE_ELEMENT
	node.children = []

	return node

# 
static func _attach_node_data(node: XMLNode, parser: XMLParser) -> void:
	# XMLParser将节点之间的空白内容视为NODE_TEXT,这是不需要的
	# 因此,我们去除了“空格”,导致只有实际的内容滑入到内容中
	node.content += parser.get_node_data().strip_edges()

static func _attach_node_cdata(node: XMLNode, parser: XMLParser) -> void:
	node.cdata.append(parser.get_node_name().strip_edges())

# 获取属性
static func _get_attributes(parser: XMLParser) -> Dictionary:
	var attrs: Dictionary = {}
	var attr_count: int = parser.get_attribute_count()

	for attr_idx in range(attr_count):
		attrs[parser.get_attribute_name(attr_idx)] = parser.get_attribute_value(attr_idx)

	return attrs


static func _cleanup_double_blankets(xml: PackedByteArray) -> PackedByteArray:
	# #XMLParser再次“不正确”,并且由于双空格转义而重复节点
	# https://github.com/godotengine/godot/issues/81896#issuecomment-1731320027

	var rm_count := 0 # How much elements (blankets) to remove from the source
	var idx := xml.size() - 1

	# 以相反的顺序迭代。这对perf很重要,否则我们
	# 需要执行double.reverse()并从一开始就删除元素
	# 这两种阵列都相当昂贵
	while idx >= 0:
		if xml[idx] in [9, 10, 13]: # [\t, \n, \r]
			rm_count += 1
			idx -= 1
		else:
			break

	# 移除空格
	while rm_count > 0:
		xml.remove_at(xml.size() - 1)
		rm_count -= 1

	return xml

XMLDocument

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6J3w9fdL-1721699899309)(https://i-blog.csdnimg.cn/direct/fb4373b9eeda4c02b522d65970bcd396.png)]

xml_document.gd中申明了一个XMLDocument类,用于表示XML文档,只提供一个root属性表示XML文档的根节点。

# ==================================================================
# 名称:XMLDocument
# 描述:表示XML文档
# 原作者:elenakrittik(https://github.com/elenakrittik)
# 翻译+重排:巽星石 最后修改:202472115:38:23
# ==================================================================
class_name XMLDocument extends RefCounted
var root: XMLNode
func _to_string():
	return "<XMLDocument root=%s>" % self.root

XMLNode

可以看到比较核心和重要的内容放到了XMLNode类上。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I6v0DgEY-1721699899309)(https://i-blog.csdnimg.cn/direct/37345cf497eb44afa87f6d4d880153c8.png)]

# ==================================================================
# 名称:XMLNode
# 描述:表示XML元素(又名XML节点)。
# 提示:如果只有一个同名的子节点,您可以通过 this_node.my_child_name 访问它!
# 原作者:elenakrittik(https://github.com/elenakrittik)
# 翻译+重排:巽星石 最后修改:202472115:38:23
# ==================================================================
class_name XMLNode extends RefCounted
# ========================================== 属性 ==========================================
var name: String = ""             # 节点名称
var attributes: Dictionary = {}   # 节点属性字典
var content: String = ""          # 节点内容
var cdata: Array[String] = []     # CDATA
var standalone: bool = false      # 是否为空节点(也就是自闭合标签)
var children: Array[XMLNode] = [] # 子节点

var _node_props: Array
var _node_props_initialized: bool = false

const KNOWN_PROPERTIES: Array[String] = ["name", "attributes", "content", "cdata", "standalone", "children"]
# ========================================== 虚函数 ==========================================
# print()或调用to_string()时返回的文本
func _to_string():
	return "<XMLNode name=%s attributes=%s content=%s cdata=%s standalone=%s children=%s>" % [
		self.name,
		"{...}" if len(self.attributes) > 0 else "{}",
		'"..."' if len(self.content) > 0 else '""',
		"[...]" if len(self.cdata) > 0 else "[]",
		self.standalone,
		"[...]" if len(self.children) > 0 else "[]"
	]

# 通过GDScript进行点访问
func _get(property: StringName):
	if not self._node_props_initialized:
		self._initialize_node_properties()

	if (
		property not in KNOWN_PROPERTIES
		and property in self._node_props
	):
		for child in self.children:
			if child.name == property:
				return child

# 通过编辑器进行点式访问
func _get_property_list() -> Array[Dictionary]:
	var props: Array[Dictionary] = []

	if not self._node_props_initialized:
		self._initialize_node_properties()

	for child_name in self._node_props:
		props.append({
			"name": child_name,
			"type": TYPE_OBJECT,
			"class_name": "XMLNode",
			"usage": PROPERTY_USAGE_DEFAULT,
			"hint": PROPERTY_HINT_NONE,
			"hint_string": "",
		})

	return props

# 初始化节点属性
func _initialize_node_properties() -> void:
	var names_to_nodes := {}

	for child: XMLNode in self.children:
		if not child.name in names_to_nodes.keys():
			names_to_nodes[child.name] = child
		else:
			names_to_nodes.erase(child.name)

	self._node_props = names_to_nodes.keys()
	self._node_props_initialized = true
# ========================================== 方法 ==========================================
# 将当前节点(及其所有子节点)转换为字典形式
func to_dict() -> Dictionary:
	var output := {}
	output["__name__"] = self.name         # 节点名称
	output["__content__"] = self.content   # 节点内容
	output["__cdata__"] = self.cdata       # CDATA
	output["attrs"] = self.attributes      # 节点属性
	# 遍历+递归获取子节点的字典形式
	var children_dict := {}
	for child in self.children:
		children_dict[child.name] = child.to_dict()
	output["children"] = children_dict
	return output

# -------------------- 类型转化 -------------------- 
# 将当前节点转储到指定文件
func dump_file(path: String,pretty: bool = false,indent_level: int = 0,indent_length: int = 2) -> void:
	var file = FileAccess.open(path, FileAccess.WRITE)
	var xml: String = self.dump_str(pretty, indent_level, indent_length)
	file.store_string(xml)
	file = null

# 将当前节点转化为 PackedByteArray 形式
func dump_buffer(pretty: bool = false,indent_level: int = 0,indent_length: int = 2) -> PackedByteArray:
	return self.dump_str(pretty, indent_level, indent_length).to_utf8_buffer()


# 将当前节点转化为 String 形式。
func dump_str(pretty: bool = false,indent_level: int = 0,indent_length: int = 2) -> String:
	if indent_level < 0:
		push_warning("indent_level必须>= 0")
		indent_level = 0
		
	if indent_length < 0:
		push_warning("indent_length必须>= 0")
		indent_length = 0

	return self._dump() if not pretty else self._dump_pretty(indent_level, indent_length)

# -------------------- 类型转化核心函数(底层、被调用) -------------------- 
# 转化核心函数
func _dump() -> String:
	var attribute_string := ""
	var children_string := ""
	var cdata_string = ""

	if not self.attributes.is_empty():
		attribute_string += " "

		for attribute_key in self.attributes:
			var attribute_value := self.attributes.get(attribute_key)

			if attribute_value is String:
				attribute_value = attribute_value.xml_escape(true)

			attribute_string += '{key}="{value}"'.format({"key": attribute_key, "value": attribute_value})

	for child: XMLNode in self.children:
		children_string += child._dump()

	for cdata_content in self.cdata:
		cdata_string += "<![CDATA[%s]]>" % cdata_content.replace("]]>", "]]]]><![CDATA[>")

	if self.standalone:
		return "<" + self.name + attribute_string + "/>"
	else:
		return (
			"<" + self.name + attribute_string + ">" +
			self.content.xml_escape() + cdata_string + children_string +
			"</" + self.name + ">"
		)
# -------------------- 格式化 -------------------- 
# 格式化XML文档
# 让杂乱的内容变的清晰美观
func _dump_pretty(indent_level: int, indent_length: int) -> String:
	var indent_string := " ".repeat(indent_level * indent_length)
	var indent_next_string := indent_string + " ".repeat(indent_length)
	var attribute_string := ""
	var content_string := "\n" + indent_next_string + self.content.xml_escape() if not self.content.is_empty() else ""
	var children_string := ""
	var cdata_string := ""

	if not self.attributes.is_empty():
		for attribute_key in self.attributes:
			var attribute_value := self.attributes.get(attribute_key)

			if attribute_value is String:
				attribute_value = attribute_value.xml_escape(true)

			attribute_string += ' {key}="{value}"'.format({"key": attribute_key, "value": attribute_value})

	for child: XMLNode in self.children:
		children_string += "\n" + child.dump_str(true, indent_level + 1, indent_length)

	for cdata_content in self.cdata:
		cdata_string += "\n" + indent_next_string + (
			"<![CDATA[%s]]>" % cdata_content.replace("]]>", "]]]]>\n%s<![CDATA[>" % indent_next_string)
		)

	if self.standalone:
		return indent_string + "<" + self.name + attribute_string + "/>"
	else:
		return (
			indent_string + "<" + self.name + attribute_string + ">" +
			content_string + cdata_string + children_string +
			"\n" + indent_string + "</" + self.name + ">"
		)

使用

解析XML文件

@tool
extends EditorScript

func _run() -> void:
	var xml_doc:XMLDocument = XML.parse_file("icon.svg")  # 解析SVG文件为XMLDocument实例
	print(xml_doc)
	pass

上面代码中:

  • 我们使用XML静态函数库的parse_file()静态方法,将Godot项目根目录的icon.svg解析为XMLDocument实例
  • print(xml_doc)会自动调用XMLDocument内部的_to_string()并打印其返回值

输出:

<XMLDocument root=<XMLNode name=svg attributes={...} content="" cdata=[] standalone=false children=[...]>>

解析XML并转为字典形式

@tool
extends EditorScript

func _run() -> void:
	var xml_doc:XMLDocument = XML.parse_file("icon.svg")  # 解析SVG文件为XMLDocument实例
	var root:XMLNode = xml_doc.root          # 获取根节点
	var dict:Dictionary = root.to_dict()     # 转化为字典形式
	print(JSON.stringify(dict,"\t") )        # 以格式化的JSON形式打印

上面代码中:

  • 使用XMLNodeto_dict()方法会将当前节点转化为字典,其内部会采用递归形式将其所有子孙节点转化为字典放入对应父节点的children属性中。
  • XMLDocumentroot属性记录了解析后的XML文档根节点,我们只需要对这个根节点调用to_dict()方法,就可以获得整个XML文档的字典形式

上面代码输出:

{
	"__cdata__": [],
	"__content__": "",
	"__name__": "svg",
	"attrs": {
		"height": "128",
		"width": "128",
		"xmlns": "http://www.w3.org/2000/svg"
	},
	"children": {
		"g": {
			"__cdata__": [],
			"__content__": "",
			"__name__": "g",
			"attrs": {
				"transform": "scale(.101) translate(122 122)"
			},
			"children": {
				"g": {
					"__cdata__": [],
					"__content__": "",
					"__name__": "g",
					"attrs": {
						"fill": "#414042"
					},
					"children": {
						"circle": {
							"__cdata__": [],
							"__content__": "",
							"__name__": "circle",
							"attrs": {
								"cx": "717",
								"cy": "532",
								"r": "60"
							},
							"children": {

							}
						}
					}
				}
			}
		},
		"rect": {
			"__cdata__": [],
			"__content__": "",
			"__name__": "rect",
			"attrs": {
				"fill": "#363d52",
				"height": "124",
				"rx": "14",
				"stroke": "#212532",
				"stroke-width": "4",
				"width": "124",
				"x": "2",
				"y": "2"
			},
			"children": {

			}
		}
	}
}

可以看到,每个节点主要结构:

  • __name__:存储XML节点名称
  • __content__:存储XML节点内部的文本
  • __cdata__:存储CDATD
  • attrs:以字典和键值对形式存储XML节点的属性和值
  • children以字典形式存储XML节点的子节点

构造XML文件

搞定XML解析和转化之后,我们也可以用代码形式生成XML文件。

@tool
extends EditorScript

func _run() -> void:
	var xml_doc:= XMLDocument.new()
	# 根节点svg
	var root = XMLNode.new()
	root.name = "svg"
	root.attributes = {
		"width":200,
		"height":200
	}
	# 子节点rect
	var rect = XMLNode.new()
	rect.name = "rect"
	rect.attributes = {
		"width":200,
		"height":200,
		"fill":"red"
	}
	
	root.children.append(rect)    # 添加为根节点的子节点
	xml_doc.root = root           # 设为文档根节点
	
	print(XML.dump_str(xml_doc))  # 转为文本形式

上面的代码中:

  • 我们利用XMLDocumentnew()方法创建了一个XMLDocument实例xml_doc
  • 接着我们用XMLNodenew()方法创建根节点root和其子节点rect,并设定相应的属性。
  • 最后将rect作为子节点添加到rootchildren中去,再指定xml_doc的根节点rootroot
  • 最后通过XML函数库的dump_str方法,将xml_doc转为字符串形式并打印。

打印输出:

<svg width="200"height="200"><rect width="200"height="200"fill="red"></rect></svg>

格式化

dump_str()方法的pretty参数设为true:

print(XML.dump_str(xml_doc,true))  # 转为文本形式

则会进行文档格式化,输出效果:

<svg width="200" height="200">
  <rect width="200" height="200" fill="red">
  </rect>
</svg>

可以看到这里默认把<rect>设定为双标签了。我们只需要设定rectstandalone属性等于true就可以了。

输出:

<svg width="200" height="200">
  <rect width="200" height="200" fill="red"/>
</svg>

存储文件

要将生成的XML或SVG内容存储为文件,只需要调用XML静态函数库的dump_file()方法就可以了。

XML.dump_file("test.svg",xml_doc,true)  # 转为文本形式

打开后:
image.png

我们只需要给根节点<svg>标签加上xmlns属性和其固定值http://www.w3.org/2000/svg就可以了。

# 根节点svg
	var root = XMLNode.new()
	root.name = "svg"
	root.attributes = {
		"width":200,
		"height":200,
		"xmlns":"http://www.w3.org/2000/svg"
	}

此时生成的SVG文件就可以正常查看了。
image.png
也可以直接在Godot中使用了:

image.png

个人心声

之所以研究SVG,是因为:

  • 可以在Godot中作为美术素材使用,本质上是纯文本,而且是参数化和矢量化的
  • SVG与Godot内置绘图函数的参数化形式最为接近,理论上SVG可以与绘图函数互转
  • 可以用函数或代码形式动态生成SVG图形,并赋值给节点
  • 对于TextureButtonTextureProgress节点,可以参数化生成其所需的图片元素
  • 可以将碰撞图形、Polygon2DLine2DPath2D等的形状和路径信息转为SVG形式存储和修改

总结

  • Godot中的XMLParser类只提供了基础的API来解析XML文件,如果需要像ConfigFileJSON一样容易使用,你就需要自己动手编写一个解析器
  • 而GodotXML,低级别的可以直接“拿来主义”,用就完事儿,也可以研究研究源码,再其基础上进行改进或二次设计。
  • 在GodotXML基础上可以进一步编写类或函数库,实现更多功能

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Web开发:图片九宫格与非九宫格动态切换效果(HTML、CSS、JavaScript)

目录 一、业务需求 二、实现思路 三、实现过程 1、基础页面 2、图片大小调整 3、图片位置调整 4、鼠标控制切换 5、添加过渡 四、完整代码 一、业务需求 默认显示基础图片&#xff1b; 当鼠标移入&#xff0c;使用九宫格效果展示图片&#xff1b; 当鼠标离开&#…

基于Cobbler实现多版本系统批量部署

一、实验题目 基于Cobbler实现多版本系统批量部署 二、实验目的 通过Cobbler&#xff0c;实验旨在实现无需人工干预即可自动安装多个版本的操作系统。这可以大大提高机房设备或服务器集群的部署效率&#xff0c;减少人力成本和操作错误。 三、实验环境 centos7.9并安装Cob…

机器学习 | 回归算法原理——多重回归

Hi&#xff0c;大家好&#xff0c;我是半亩花海。接着上次的多项式回归继续更新《白话机器学习的数学》这本书的学习笔记&#xff0c;在此分享多重回归这一回归算法原理。本章的回归算法原理基于《基于广告费预测点击量》项目&#xff0c;欢迎大家交流学习&#xff01; 目录 一…

【Django】anaconda环境变量配置及配置python虚拟环境

文章目录 配置环境变量配置python虚拟环境查看conda源并配置国内源在虚拟环境中安装django 配置环境变量 control sysdm.cpl,,3笔者anaconda安装目录为C:\ProgramData\anaconda3 那么需要加入path中的有如下三个 C:\ProgramData\anaconda3 C:\ProgramData\anaconda3\Scripts C:…

【数据结构】搜索二叉树

二叉搜索树 二叉树的博客 在之前的数据结构的文章中已经基本对二叉树有一定的了解&#xff0c;二叉搜索树也是一种数据结构&#xff0c;下面将对二叉搜索树进行讲解。 二叉搜索树的概念 二叉搜索树又称为二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有下面性…

【微软蓝屏】微软Windows蓝屏问题汇总与应对解决策略

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

HTML常见标签——超链接a标签

一、a标签简介 二、a标签属性 href属性 target属性 三、a标签的作用 利用a标签进行页面跳转 利用a标签返回页面顶部以及跳转页面指定区域 利用a标签实现文件下载 一、a标签简介 <a>标签用于做跳转、导航&#xff0c;是双标签&#xff0c;记作<a></a>&#…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第三十九章 Linux MISC驱动

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

MongoDB教程(十八):MongoDB MapReduce

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; 文章目录 引言一、MapRed…

上传项目到GitHub

上传项目到GitHub 前期工作&#xff1a;创建GitHub仓库 1.使用git命令初始化文件夹 git init2.将文件夹里面所有的文件添加到本地仓库&#xff0c;如果想添加单个文件&#xff0c;将.换成文件名就好。 git add .3.给文件备注&#xff0c;双引号里面是文件备注的内容 git c…

8月开始|《660》+《880》45天带刷计划!

660880题目加起来挺多了&#xff01; 如果是一刷加上二刷错题的话&#xff0c;总共需要做2000道题左右。 如果全都吃透的话&#xff0c;按照传统的刷题方法&#xff0c;慢的话需要2个多月&#xff0c;差不多就是每天30多道题的量 快的话&#xff0c;大概就是一个月就能刷完&…

低代码如何加速数字化转型

数字化转型&#xff0c;正日益决定企业成功的关键。这里的一个关键因素是它可以以更快的速度和质量来实施技术计划。在当今瞬息万变的商业环境中&#xff0c;战略性地采用低代码平台对于旨在加快上市时间、增强业务敏捷性和促进跨团队无缝协作的首席技术官来说至关重要。日益增…

【教程】vscode添加powershell7终端

win10自带的 powershell 是1.0版本的&#xff0c;太老了&#xff0c;更换为powershell7后&#xff0c;在 vscode 的集成终端中没有显示本篇教程记录在vscode添加powershell7终端的过程 打开vscode终端配置 然后来到这个页面进行设置 查看 powershell7 的安装位置&#xff…

Linux开启coredump

在Linux系统中&#xff0c;C/C程序崩溃是常见的问题之一。Coredump是指当一个程序崩溃时&#xff0c;系统把程序运行时的内存数据以二进制文件的形式保存下来&#xff0c;以便程序开发者进行崩溃分析。本文将介绍如何开启并配置Coredump 1、查看并配置coredump 在Linux系统中…

Git仓库拆分和Merge

1. 问题背景 我们原先有一个项目叫open-api&#xff0c;后来想要做租户独立发展&#xff0c;每个租户独立成一个项目&#xff0c;比如租户akc独立部署一个akc-open-api&#xff0c;租户yhd独立部署一个yhd-open-api&#xff0c;其中大部分代码是相同的&#xff0c;少量租户定制…

昇思25天学习打卡营第20天|CV-ResNet50图像分类

打卡 目录 打卡 图像分类 ResNet网络介绍 数据集准备与加载 可视化部分数据集 残差网络构建 Building Block 结构 代码实现 Bottleneck结构 代码实现 构建ResNet50网络 代码定义 模型训练与评估 可视化模型预测 重点&#xff1a;通过网络层数加深&#xff0c;感知…

Docker-Compose配置zookeeper+KaFka+CMAK简单集群

1. 本地DNS解析管理 # 编辑hosts文件 sudo nano /etc/hosts # 添加以下三个主机IP 192.168.186.77 zoo1 k1 192.168.186.18 zoo2 k2 192.168.186.216 zoo3 k3注&#xff1a;zoo1是192.168.186.77的别名&#xff0c;zoo2是192.168.186.18的别名&#xff0c;zoo3是192.168.186.1…

【HarmonyOS】应用推送使用个推SDK如何实现?

【HarmonyOS】应用推送使用个推SDK如何实现&#xff1f; 前言 个推和极光都是市面上很成熟的推送第三方SDK了。今天讲讲个推SDK在鸿蒙中如何集成使用。 存在即合理&#xff0c;三方SDK推送给我们带来了极大的好处&#xff0c;首先在服务器后台处理一套API就可搞定&#xff0…

敏捷CSM认证:精通敏捷Scum估算方法,高效完成项目!

咱们做项目的时候可能都遇到过这种情况&#xff1a;项目一开始信心满满&#xff0c;觉得 deadline 稳了。结果呢&#xff1f;各种意外状况频出&#xff0c;时间好像怎么都不够用了&#xff0c;最后项目只能无奈延期&#xff0c;整个团队都像霜打的茄子。 说到底&#xff0c;还…

Mamba-yolo|结合Mamba注意力机制的视觉检测

一、本文介绍 PDF地址&#xff1a;https://arxiv.org/pdf/2405.16605v1 代码地址&#xff1a;GitHub - LeapLabTHU/MLLA: Official repository of MLLA Demystify Mamba in Vision: A Linear AttentionPerspective一文中引入Baseline Mamba&#xff0c;指明Mamba在处理各种高…