当前位置: 首页 > news >正文

使用kotlin编写html dsl框架

前排提醒,这个框架就是我写着玩的,如果您已经会使用vue或其他前端框架,这篇文章可能对您没有什么意义。即使您不会如上提到的框架,也不要对该框架报有过高的期待,该框架更多的是,我自己的自娱自乐。

这里还要提醒一下,该框架没有实现对css和js的支持,就是一个生成html代码的工具。

  • 前言
  • 使用和演示
  • 代码
  • 后记

前言

为什么我要写这个玩意出来?因为我有时想用网页写一写游戏评测文章,而用html编写就可以比较方便地通过浏览器在不同网页之间跳转。如果编写markdown文章或其他方式,就比较麻烦了。
而我作为一名安卓开发者,对前端开发并不熟悉,而且对网页的要求也不高。只需要可显示不同样式的文字、图片和链接等功能,所以就不想花精力去学习前端框架。当然了,以后如果工作需要,那去学习也不可厚非。
编写该框架确实有如上的原因,但还有一个原因:我想试试看我能不能写出来。所以就经历了框架设计、改进框架不合理的代码等过程,最后有了现在代码。

使用和演示

上面说了那么多,接下来贴一下使用代码。

html {header {title("test")}body {div {a {href = "https://www.baidu.com"text = "baidu"}h1("这是h1")text("text")}text("text")}
}.toHtmlCode().also(::println)// println
<html><header><title>test</title></header><body><div><a href="https://www.baidu.com">baidu</a><h1>这是h1</h1>text<img/></div>text</body></html>

可以看到,只需很简单的编写代码,就能够输出完整的html代码,而不需要手写很多标签代码。
再看看我使用该框架编写html的实际代码和最终效果。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

代码

  • HtmlTag
  • HtmlBody
  • getAttributeList
  • 纯文本
  • dsld代码
  • 字数统计

上面代码看似简单,但实际上,调用的代码一点都不少,想看实现的代码,可以直接看这个github链接,接下来是代码解析

HtmlTag

首先,需要一个类来描述html代码,所以我就创建了HtmlTag,任何一个标签都是HtmlTag的实现类。
interface HtmlTag {fun getTagString(): String// 获取标签的属性,如id、width、height等// Pair的first就是属性的字符串,second就是属性的值,类型设计为Any是考虑到Int、Double这些类型。如果限制为String,那每次都需要手动调用一次toString,这样做其实挺麻烦的fun getAttributeList(): List<Pair<String, Any>>// 转换成html代码,每个标签做好自己的转换任务,由上级调用下级的该方法进行转换,最终形成一条调用链,这样就能非常方便地生成html代码fun toHtmlCode(): String
}

从上面的演示代码可以看到,还有html这个方法,这个方法获取到的就是一个HtmlRoot对象,看看该对象里面有什么代码

class HtmlRoot: HtmlTag {var header: HtmlHeaderRoot? = nullvar body: HtmlBodyRoot? = nulloverride fun getTagString(): String = TAGoverride fun toHtmlCode(): String {return generateHtmlCode(listOfNotNull(header, body))}override fun getAttributeList(): List<Pair<String, Any>> = emptyList()companion object {const val TAG = "html"}
}

这里的generateHtmlCode是一个扩展方法,下面会提到,先把注意力放到其他地方。

HtmlBody

可以看到,该类里面,有header和body对象,header和body的代码类似,我就把body拿出来讲。
不过在看看body的代码之前,先了解一下body的基类。
HtmlBody

interface HtmlBody: HtmlTag

就一个非常简单的接口,所有和body有关的代码,都必须为该接口的实现类,包括HtmlBodyRoot本身。该类有两个直接实现类:HtmlBodySingleHtmlBodyGroup
HtmlBodySingle

abstract class HtmlBodySingle<T: HtmlBody>: HtmlBody {open var body: T? = null
}

HtmlBodyGroup

abstract class HtmlBodyGroup<T: HtmlBody>: HtmlBody {protected open var internalBodyList: MutableList<T> = ArrayList()open fun addHtmlBody(body: T) {internalBodyList.add(body)}open fun addAllHtmlBody(list: List<T>) {internalBodyList.addAll(list)}open fun removeHtmlBody(body: T) {internalBodyList.remove(body)}open fun clearBodyList() {internalBodyList.clear()}open fun getBodyList(): List<T> {return internalBodyList}
}

可以看到,single只能有一个body,而group可以有多个body。为什么要这样做?因为我考虑到有些标签只能有一个子标签,如a、h这些标签,而有一些则可以多个,如body、div等。
一开始,我考虑的是,将single作为一个特殊的group标签,做一些实现,保证开发者永远只能在list里面添加一个标签,就像android的ScrollView一样。但随后想了想,还是不要这样做,因为这样会增加开发者出错的几率。
上面还有泛型这个东西,我考虑的是,有一些标签的子标签类型不需要那么宽泛,只需某些特定的标签,如table、ul、ol等,所以我就加了一个泛型上去。
现在再看看body标签的代码

class HtmlBodyRoot: HtmlBodyGroup<HtmlBody>() {var style: HtmlStyleRoot? = nulloverride fun getTagString(): String = TAG// 这里就是将style和bodyList转换成String,生成一个字符串List,传递给generateHtmlCodeByStringList方法去生成html代码override fun toHtmlCode(): String {val style = style?.toStyleCode()?.takeIf { it.isNotEmpty() } ?: ""return generateHtmlCodeByStringList(listOf(style, getBodyList().bodyTagListToString()))}// 我的印象里,body好像没有属性,所以我直接返回一个空Listoverride fun getAttributeList(): List<Pair<String, Any>> = emptyList()companion object{const val TAG = "body"}
}fun <T: HtmlBody> List<T>.bodyTagListToString() = joinToString("") { it.toHtmlCode() }

可以看到,body的代码也没什么,就是一些很普通的代码。
我贴出来的代码有不少调用generateHtmlCode这个方法,来看看该方法的实现代码。

fun HtmlTag.generateHtmlCode(): String {return generateHtmlCode("")
}// 大部分body会调用这里的single和group的扩展方法,可以看到,这里的代码最终都会调用toHtmlCode方法
fun <T: HtmlBody> HtmlBodySingle<T>.generateHtmlCode(): String {return generateHtmlCode(body?.toHtmlCode() ?: "")
}fun <T: HtmlBody> HtmlBodyGroup<T>.generateHtmlCode(): String {return generateHtmlCode(getBodyList().bodyTagListToString())
}fun <T: HtmlHeader> HtmlHeaderGroup<T>.generateHtmlCode(): String {return generateHtmlCode(getHeaderList().headerTagListToString())
}fun <T: HtmlTag> HtmlTag.generateHtmlCode(htmlList: List<T>): String {return generateHtmlCode(htmlList.tagListToString())}// HtmlBodyRoot就是调用这个方法生成html代码
fun HtmlTag.generateHtmlCodeByStringList(codeList: List<String>): String {// 这里也只是将String List调用joinToString转换成一个String而已return generateHtmlCode(codeList.joinToString(""))
}// 所有代码最终都会调用该方法,可以直接看该方法的代码
fun HtmlTag.generateHtmlCode(value: String): String {// 这里就会调用HtmlTag的getAttributeList方法,将它们转换成first="second"这样的代码,并在转换后的字符串前面加一个空格。如果该List为空,就直接返回空字符串。// 为什么要加一个空格,因为如果不加空额,标签代码就会和属性代码贴在一起。如:<ahref=""/>val attributeString = getAttributeList().takeIf { it.isNotEmpty() }?.joinToString(" ") {"${it.first}=\"${it.second}\""}?.let { " $it" } ?: ""// 如果value为空,就生成闭标签的代码,否则就生成开标签的代码return if(value.isEmpty()) {"<${getTagString()}$attributeString/>"} else {buildString {// 配合上面的single和group的generateHtmlCode扩展方法,就不难理解// 将子body的代码包在自己的标签里面,而子body也会调用自己的子body包在自己的标签里面// 最后一层包一层,就可以输出一串复杂的html代码append("<${getTagString()}$attributeString>")append(value)append("</${getTagString()}>")}}
}

为什么要将它们作为扩展方法,而不是放到HtmlTag里面?因为这里面有一些方法不是每个实现类都用得上。如果将这些代码放到顶层接口里面,最终会导致每个实现类生成的class文件里面,有很多用不上的代码,所以将这些代码作为扩展方法是比较实际的。而且接口更多是起到定义规范的作用,而不是当工具类使用。

getAttributeList

再提一下getAttributeList的实现,拿a标签举例
HtmlBodyGeneralAttribute.kt

// 属性的根类是HtmlBodyGeneralAttribute,这里面包含了所有基础标签,不过我只是将开发中需要的属性加上去,后续如果还需要其他属性,可以自己加
interface HtmlBodyGeneralAttribute<T: HtmlBodyGeneralAttributeEntity> {// 为属性提供默认实现,这样做了之后,实现类就不用手动写这些代码了// 这里的attributeEntity就是存放这些属性的实体,如果在这里直接给这个字段编写get方法,每次调用该字段都会重写new一个对象// 所以只能交给实现类去new,但也仅仅需要编写这一行代码,所以我认为这没有什么负担val attributeEntity: T// 通过字段的形式来设置属性,这样外部用起来就比较方便,而不用去调用方法// 这些字段默认是否为null,就自己判断了,如果觉得不需要null,也可以将?去掉var id: String?get() = attributeEntity.idset(value) {attributeEntity.id = value}var width: String?get() = attributeEntity.widthset(value) {attributeEntity.width = value}var height: String?get() = attributeEntity.heightset(value) {attributeEntity.height = value}// 最后通过该方法生成一个Pair List// toPairByStringValue的代码已经放在下面了,该方法会判断String Value是否为空字符串,如果是,就烦恼会一个空的Pair对象// 这里调用listOfNotNull,所以空的Pair对象就会直接被过滤掉fun getAttributeList(): List<Pair<String, Any>> {return listOfNotNull(HtmlBodyAttribute.general.ID toPairByStringValue attributeEntity.id,HtmlBodyAttribute.general.WIDTH toPairByStringValue attributeEntity.width,HtmlBodyAttribute.general.HEIGHT toPairByStringValue attributeEntity.height,)}
}open class HtmlBodyGeneralAttributeEntity {var id: String? = nullvar width: String? = nullvar height: String? = null
}infix fun String.toPairByStringValue(value: String?): Pair<String, String>? {return value?.takeIf { it.isNotEmpty() }?.let { this to value }
}

HtmlBodyATag.kt

// 这个类用于存放a标签需要的属性,该类需要继承HtmlBodyGeneralAttribute,并提供attibuteEntity对象类型
// 所以就写一个a的attibuteEntity继承GenernalAttributeEntity
interface HtmlBodyAAttribute: HtmlBodyGeneralAttribute<HtmlBodyAAttributeEntity> {var href: String?get() = attributeEntity.hrefset(value) {attributeEntity.href = value}var target: String?get() = attributeEntity.targetset(value) {attributeEntity.target = value}// 重点是这里,继承之后,需要重写该方法,将super的结果取出来,并放一个新的List,形成一个二维List// 最后再调用list的flatten方法, 将二维List变成一维// 当然了,如果觉得这种实现方法不太好,也可以自己换一种更好的实现方式override fun getAttributeList(): List<Pair<String, Any>> {val superList = super.getAttributeList()val currentList = listOfNotNull(HtmlBodyAttribute.a.HREF toPairByStringValue attributeEntity.href,HtmlBodyAttribute.a.TARGET toPairByStringValue attributeEntity.target,)return listOf(superList, currentList).flatten()}
}class HtmlBodyAAttributeEntity: HtmlBodyGeneralAttributeEntity() {var href: String? = nullvar target: String? = null
}// 实现HtmlBodyAAttribute接口
class HtmlBodyATag: HtmlBodySingle<HtmlBody>(), HtmlBodyAAttribute {// 在a标签里面,只需重写这个字段,其他的都不用做,就拥有了设置id、width、href等功能override val attributeEntity: HtmlBodyAAttributeEntity = HtmlBodyAAttributeEntity()override fun getTagString(): String = TAGoverride fun toHtmlCode(): String {return generateHtmlCode()}// 这里需要override是没办法的事情,因为HtmlTag本身也有一个名称一样的方法,并且没有提供实现// 这里调用super就可以将AAttribute的实现直接拿过来用,所以override fun getAttributeList(): List<Pair<String, Any>> {return super.getAttributeList()}companion object{const val TAG = "a"}
}

纯文本

从上面的HtmlBody、HtmlBodySinge和HtmlBodyGroup的代码可以看到,没有一个属性用来编写纯文本,但html想要编写纯文本的代码,只需在空白部分编写就可以显示出来。
鉴于这种情况,我编写了HtmlBodyTextTag来实现这个功能,代码非常简单

class HtmlBodyTextTag: HtmlBody {var text: String = ""override fun getTagString(): String = ""override fun toHtmlCode(): String {return text}override fun getAttributeList(): List<Pair<String, Any>> = emptyList()
}

如果某个标签需要纯文本的内容,就可以设置这样一个body

dsl代码

上面将body的基本架构都介绍完了,接下来写一下dsl的代码是怎么写的
htmlDSL.kt

inline fun html(action: HtmlRoot.() -> Unit): HtmlRoot {return HtmlRoot().also {it.action()}
}inline fun HtmlRoot.header(action: HtmlHeaderRoot.() -> Unit): HtmlHeaderRoot {return HtmlHeaderRoot().also {it.action()header = it}
}inline fun HtmlRoot.body(action: HtmlBodyRoot.() -> Unit): HtmlBodyRoot {return HtmlBodyRoot().also {it.action()body = it}
}

代码还是比较简单的,想要写注释,但不知道要写什么
有关body dsl的代码还是有点多的,所以拿一部分出来讲
htmlBodyGenenralDSL.kt

// 给single扩展方法之后,在single里面就可以直接调用a了,而不需要用body = getA这种麻烦的形式
inline fun HtmlBodySingle<HtmlBody>.a(action: HtmlBodyATag.() -> Unit) {getA.also {it.action()body = it}
}// 由于HtmlBodyRoot也是一个HtmlBodyGroup对象,所以这样写了之后,就能为body添加一个a标签
// 返回类型:返回自己本身。为什么要这样做?因为返回自己之后,就能链式调用代码
// 比如写一个a标签之后,如果不想换行去写一个text标签,就可以直接在a标签的代码后面.去调用,否则需要手动写";"才能调用
inline fun HtmlBodyGroup<HtmlBody>.a(action: HtmlBodyATag.() -> Unit): HtmlBodyGroup<HtmlBody> {getA.also {it.action()addHtmlBody(it)}return this
}

htmlBodyGenenralGetDSL.kt

inline val getA: HtmlBodyATagget() = HtmlBodyATag()

给group扩展之后,就不只是body可以使用,像div、li、th、td这些,也都可以通过这种方法add一个body,使用起来非常方便。
从上面还能看到,有一个getA,为什么要这样做?
可以想象一下,如果外部需要new一个A标签的对象,那就需要记住A标签的对象名称,但名称又那么长,每次写起来都挺麻烦的。而如果用这种形式,就可以让开发者无需记住对象名称,需要使用时,只需在标签前面加个get就可以拿到对应的标签对象,这不是很方便吗?
大部分标签会提供get方法,并放到GetDSL里面。
text

inline fun HtmlBodySingle<HtmlBody>.text(text: String){getText.also {it.text = textbody = it}
}var HtmlBodySingle<HtmlBody>.text: Stringget() = (body as? HtmlBodyTextTag)?.text ?: ""set(value) {val body = body as? HtmlBodyTextTag ?: getText.also {body = it}body.text = value}inline fun HtmlBodyGroup<HtmlBody>.text(text: String): HtmlBodyGroup<HtmlBody> {getText.also {it.text = textaddHtmlBody(it)}return this
}

text除了提供2个方法,还为single提供了text这个字段,这样如果想要给某个标签设置text,就可以通过这种方式
dsl的代码基本都是a、text标签这种形式,其他代码都是大同小异,所以其他代码我就不贴出来了。

字数统计

最后补充一个字数统计的功能,如果要统计一个网页的字数,可以使用js进行统计,但该框架没有提供js相关的api,所以写起来有点麻烦,自然地,我也不打算用js去统计字数。
除了js,我还想到用正则表达式,将html的文本找出来,但试了几个正则表达式,我都没能将文本找出来。
最后就想到,直接提取body里面所有text对象,并获取text对象的文本内容,最后计算出字数。
fun HtmlRoot.getTextLength(): Int {// 通过递归调用,就可以获取到所有text对象,获取完成后,就可以通过text里面的text字段计算字数return body?.getTextBodyList()?.sumOf {it.text.length} ?: 0
}fun HtmlBody.getTextBodyList(): List<HtmlBodyTextTag>? {return when(this) {is HtmlBodyTextTag -> {arrayListOf(this)}is HtmlBodySingle<*> -> {getTextBodyList()}is HtmlBodyGroup<*> -> {getTextBodyList()}else -> null}
}fun HtmlBodyGroup<*>.getTextBodyList(): List<HtmlBodyTextTag>? {val list = ArrayList<HtmlBodyTextTag>()getBodyList().forEach {// 这里就会调用上面的HtmlBody的getTextBodyList方法// 如果获取到一个空对象,就不会addAll到list里面it.getTextBodyList()?.also(list::addAll)}// 如果list不为空,就返回该List,否则就返回nullreturn list.takeIf { it.isNotEmpty() }
}fun HtmlBodySingle<*>.getTextBodyList(): List<HtmlBodyTextTag>? {return body?.getTextBodyList()
}

我自己没有想出来如何用正则表达式统计字数,所以抱着试一试的想法问了chatGPT,想不到还真得到我想要的代码。而且比我想象中的简单

在这里插入图片描述

fun getTextByHtmlCode(html: String): String {// 移除 HTML 标签var text = html.replace("\\<.*?\\>".toRegex(), "")// 移除 HTML 转义字符text = text.replace("&.*?;".toRegex(), "")// 移除多余空格和换行符text = text.trim { it <= ' ' }.replace("[\\s\\t]+".toRegex(), " ")return text
}

可以看到,直接暴力匹配<>这个括号就行,不管里面有什么内容。而且我也用一段5000多文本的html页面测试过了,得到的结果时一样的,所以代码是不存在什么问题的。只不过执行效率我就不清楚了,我不知道正则表达式的替换效率是怎么样的。而我提供的代码,本质是递归调用,也好不到哪去。
从chatGPT发送的代码可以看到,最后还会将字符串转换成字符数组,这段在我看来是没必要的,所以我就去掉了。

后记

上面简单地讲解了该框架的使用方法和实现方式,其他代码麻烦看看github里面的代码。
由于该框架主要是为了方便自己,所以提供的标签和属性都不全,如果自己需要哪个标签,可以根据我编写的代码,自己做一套实现。模板代码已经写出来了,剩下的就是体力活了。

相关文章:

使用kotlin编写html dsl框架

前排提醒&#xff0c;这个框架就是我写着玩的&#xff0c;如果您已经会使用vue或其他前端框架&#xff0c;这篇文章可能对您没有什么意义。即使您不会如上提到的框架&#xff0c;也不要对该框架报有过高的期待&#xff0c;该框架更多的是&#xff0c;我自己的自娱自乐。 这里还…...

【谷粒学院】MybatisPlus(1~17)

1.项目介绍 2.项目背景介绍 3.项目商业模式介绍 4.项目功能模块介绍 5.项目技术点介绍 6.项目技术点-MybatisPlus介绍 官网&#xff1a;http://mp.baomidou.com/ MyBatis-Plus&#xff08;简称 MP&#xff09;是一个 MyBatis 的增强工具&#xff0c;在 MyBatis 的基础上只做…...

C++的输入输出

目录 一、基本的输入输出 二、I/O库头文件 三、标准输出流&#xff08;cout&#xff09; 四、标准输入流&#xff08;cin&#xff09; 五、标准错误流&#xff08;cerr&#xff09; 六、标准日志流&#xff08;clog&#xff09; 一、基本的输入输出 C 标准库提供了一组丰…...

RNN相关知识总结

目录RNN结构与原理1.模型总览2.反向传播LSTM结构与原理1.模型总览2.如何解决RNN梯度消失/爆炸问题&#xff1f;GRU结构及原理1.模型总览LSTM与GRU的区别RNN结构与原理 1.模型总览 上图是RNN的展开结构图&#xff0c;由输入层、隐藏层和输出层组成。当前时间步t 的隐藏状态hth_…...

2. 应用C/C++编写程序

2.1 一个完整的C++源程序 例题是参考书《C++程序设计--基础,编程抽象与算法策略》第一章的PowersOfTwo.cpp。当运行程序PowersOfTow时,计算机要询问指数是多少,即2的多少次方。例如,输入8,程序会产生2的0到8次方的值。 /* File: PowersOfTwo.cpp *程序注释 …...

Spring Boot 统一功能处理(用户登录权限效验-拦截器、异常处理、数据格式返回)

文章目录1. 统一用户登录权限效验1.1 最初用户登录权限效验1.2 Spring AOP 统一用户登录验证1.3 Spring 拦截器1.4 练习&#xff1a;登录拦截器1.5 拦截器实现原理1.6 统一访问前缀添加2. 统一异常处理3. 统一数据格式返回3.1 统一数据格式返回的实现3.2 ControllerAdvice 源码…...

oracle存储过程的使用

文章目录oracle存储过程的使用基本结构管理存储过程调用存储过程的方法存储过程参数关键词&#xff1a; IN 和outin/out测试案例调用in/out测试案例存储过程语法DECLARE声明关键词赋值使用in/out将值作为子程序的参数分配给变量&#xff0c;看上面的案例为布尔变量赋值表达式串…...

一些无线通信系统模型的概念

一些无线通信系统模型的概念 扩频通信,指的是系统的带宽WWW远大于其信息传输速率R(bits/s)R(bits/s)R(bits/s), 定义展频带因子BeWRB_e \frac{W}{R}Be​RW​, 易知在扩频通信系统中,BeB_eBe​远大于1. 在频率上产生如此大的冗余度,主要是为了减轻无线通信或卫星通信中经常产生…...

GAIDC 2023盛会迎来大模型论坛“主场”,百度飞桨护航大模型产业发展

‍‍‍‍2月25日-26日&#xff0c;2023全球人工智能开发者先锋大会&#xff08;GAIDC&#xff09;在上海临港举行&#xff0c;大会以“向光而行的AI开发者”为主题&#xff0c;汇聚了当前科技和产业革命中的开发者先锋力量。百度深度参与本次大会&#xff0c;飞桨联合上海市人工…...

Python编写GUI界面案例:实现免费下载器

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 本次网站&#xff1a; 本文所有模块\环境\源码\教程皆可点击文章下方名片获取此处跳转 开发环境: python 3.8 运行代码 pycharm 2022.3 辅助敲代码 模块使用&#xff1a; import parsel >>> pip install parsel…...

我的 System Verilog 学习记录(6)

引言 本文简单介绍 SystemVerilog 语言的 线程。 前文链接&#xff1a; 我的 System Verilog 学习记录&#xff08;1&#xff09; 我的 System Verilog 学习记录&#xff08;2&#xff09; 我的 System Verilog 学习记录&#xff08;3&#xff09; 我的 System Verilog 学…...

SAP 常见问题大全及问题解决大全

1.A:在公司代码分配折旧表时报错? 在公司代码分配折旧表时报错&#xff0c;提示是“3000 的公司代码分录不完全&#xff0d;参见长文本” 希望各位大侠帮我看看。 3000 的公司代码分录不完全&#xff0d;参见长文本 R: a.你把零进项税的代码分配给这个公司代码就可以了 …...

10.Quartz实现定时打分 热帖排行

1.Spring Quartz(1)简介核心组件scheduler 接口&#xff1a;核心调度工具&#xff0c;所有任务由这一接口调用job&#xff1a;定义任务&#xff0c;重写execute方法JobDetail接口&#xff1a;配置描述Trigger接口&#xff1a;什么时候运行&#xff0c;以什么样的频率运行(2)Spr…...

pandas 读取Excel 批量转换时间戳

一、安装 pip install pandas 如果出报错&#xff0c;不能运行&#xff0c;可以安装 pip install xlrd 二、 代码如下 import pandas as pd import time,datetimefile_path rC:\Users\Administrator\Desktop\携号转网测试\admin_log.xls df pd.read_excel(file_path, sheet_n…...

绕过检测之Executor内存马浅析(内存马系列篇五)

写在前面 前面已经从代码层面讲解了Tomcat的架构&#xff0c;这是内存马系列文章的第五篇&#xff0c;带来的是Tomcat Executor类型的内存马实现。有了前面第四篇中的了解&#xff0c;才能更好的看懂内存马的构造。 前置 什么是Executor Executor是一种可以在Tomcat组件之间…...

《C++模板进阶》

致前行的人&#xff1a; 要努力&#xff0c;但不要着急&#xff0c;繁花锦簇&#xff0c;硕果累累都需要过程&#xff01; 目录 前言&#xff1a; 1.非类型模板参数 1.1.概念&#xff1a; 1.2.使用注意事项 2.模板特化 2.1函数模板特化 2.2类模板特化 3.模板的分离编译 3.1什么…...

【项目管理】项目进度管理中的逻辑关系

项目的进度管理是项目核心管理之一&#xff0c;通过合理的进度安排&#xff0c;制定出科学可行的分项工期表&#xff0c;并条理清晰的显示出项目进度之间的逻辑关系。 1、目标是计划的灵魂 进度计划必须按照确定的项目总进度要求进行编制&#xff0c;了解项目总目标和整体安…...

ARM的汇编指令集

一、汇编指令 1.1 指令与伪指令 汇编的指令 指令是CPU机器指令的助记符&#xff0c;编译后会得到一串二进制机器码&#xff0c;由CPU执行 汇编的伪指令 伪指令本质上不是指令&#xff0c;它是编译器环境提供用来指导编译过程&#xff0c;编译后伪指令不会生成机器码 伪指令…...

@font-face用法超详细讲解

文章目录font-face是什么font-face基本语法urlTTFOTFEOTWOFFSVGformatfont-face用法示例font字体下载ttf-to-eot 字体转换器https://blog.csdn.net/qq_37417446/article/details/106728725 https://developer.mozilla.org/zh-CN/docs/Web/CSS/font-face font-face是什么 font-…...

[oeasy]python0095_乔布斯求职_雅达利_atari_breakout_打砖块_布什内尔_游戏机_Jobs

编码进化 回忆上次内容 上次 我们回顾了 电子游戏的历史 从 电子游戏鼻祖 双人网球到 视频游戏 PingPong再到 街机游戏 Pong 雅达利 公司 来了 嬉皮士 捣乱&#xff1f;&#x1f914; 布什内尔 会如何 应对 呢&#xff1f;&#x1f914; 布什内尔 布什内尔 本身就有点 …...

全景极简印度史

转自&#xff1a;印度简史 - 知乎 (zhihu.com)印度是世界上最早出现文明的地区之一&#xff0c;印度河是其文明的发源地。古印度文明的疆域曾包括今印度共和国、巴基斯坦、孟加拉国、阿富汗斯坦南部部分地区和尼泊尔。史前时代200万年前&#xff0c;巴基斯坦北部的希瓦利克遗址…...

《设计模式》模板方法

《设计模式》模板方法 模板方法是一种行为型设计模式&#xff0c;用于定义一个算法的框架&#xff0c;而将一些步骤的实现留给子类来完成。模板方法在基类中定义了一个模板方法&#xff0c;该方法确定了算法的基本结构&#xff0c;然后将一些步骤的实现交给子类去完成。这个模…...

Linux环境内存管理——链表

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天我们来重新审视一下Windows程序员如何学习Linux环境内存管理。由于很多程序在Windows环境下开发好后&#xff0c;还要部署到Linux服务器上去&#xff0c;所以作为Windows程序员有必要学习Linux环境的内存…...

String、StringBuffer、StringBuilder类

String类 由多个字符组成的一串数据,值一旦创建不可改变 private final char value[]; 一旦值改变,就会创建新的对象 String s "abc"; //char[] c {a,b,c}s"def"; // 并不是String的值改变,而是创建了一个新的对象s"gh";s"aaa"…...

在VScode中添加Linux中的Docker容器中的Python解释器

VScode编辑器在安装好Python插件之后会自动选择环境变量中排序最高的那一个解释器作为默认解释器&#xff0c;而想要额外添加新的Python解释器就需要自己设置。 VScode编辑器安装在本地电脑 支持Python的docker安装在远程服务器 第一步&#xff0c;在/usr/local/下新建pytho…...

无法将“django-admin”项识别为cmdlet,函数,脚本文件或可运行程序的名称问题

无法将“django admin”项识别为cmdlet&#xff0c;函数&#xff0c;脚本文件或可运行程序的名称问题 小提示&#xff1a;首先检查一下有没有拼写错误&#xff01;&#xff01;&#xff01;没有的话请继续 我们要知道django装到哪里去了 pip show django 注意&#xff1a;3.0…...

乐友商城学习笔记(十五)

无状态登陆原理 在服务器端保存session 无状态不需要session&#xff0c;把登陆状态保存在cookie中 jwtrsa token&#xff1a;登陆时&#xff0c; jwt oath2 jwt&#xff1a;头信息&#xff08;jwt&#xff09; 载荷&#xff08;用户信息&#xff0c;签发人&#xff0c;签发时…...

目标检测论文阅读:CBNet算法笔记

标题&#xff1a;CBNet: A Composite Backbone Network Architecture for Object Detection 期刊&#xff1a;TIP2022 论文地址&#xff1a;https://ieeexplore.ieee.org/document/9932281/ 官方代码&#xff1a;https://github.com/VDIGPKU/CBNetV2 作者单位&#xff1a;北京大…...

vue前端与Java后端进行跨域交互

1.后端的几种解决方法 1.在Controller上面加上CrossOrigin 2.写一个配置文件并且在Controller层加上注解CORSConfig package com.wolwo.langyage.base.util;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configurat…...

【设计模式】2.抽象工厂模式

抽象工厂模式 前面介绍的工厂方法模式中考虑的是一类产品的生产&#xff0c;如畜牧场只养动物、电视机厂只生产电视机、传智播客只培养计算机软件专业的学生等。 这些工厂只生产同种类产品&#xff0c;同种类产品称为同等级产品&#xff0c;也就是说&#xff1a;工厂方法模式…...

龙江做网站/网站都有哪些

作者&#xff1a;张楷露、张琪 封面&#xff1a;自己想吧一、基本思想的异同共同点从二者表达的含义上看&#xff0c;主成分分析法和因子分析法都寻求少数的几个变量(或因子)来综合反映全部变量(或因子)的大部分信息&#xff0c;变量虽然较原始变量少&#xff0c;但所包含的信…...

受欢迎自适应网站建设地址/优化设计官网

传感器从19世纪60年代诞生至今大约有150余年的时间&#xff0c;如今随着物联网产业的快速发展&#xff0c;对于传感器技术提出了更多、更高的要求。麦肯锡报告指出&#xff0c;到2025年&#xff0c;物联网带来的经济效益将在2&#xff0e;7万亿到6&#xff0e;2万亿美元之间&am…...

哈尔滨电子网站建设/快速优化seo

http://blog.csdn.net/yerenyuan_pku/article/details/72620101 SVN是何物 SVN是Subversion的简称&#xff0c;是一款集中式的开源版本控制系统&#xff0c;因为有着明确的权限管理机制&#xff0c;所以为目前国内绝大多数的公司所使用。 何谓集中式&#xff1a;集中式核心在于…...

吉安百度seo/求职seo推荐

目录 在Python中返回数组的最大值或忽略任何NaN的最大值 NumPy.nanmax() 方法 示例 1: 示例 2: 示例 3: 返回Python中用scimath将输入值提高到的幂的结果 NumPy.lib.scimath.power 方法 示例 1: 示例 2:...

jsp网站服务建设是什么/网站自动推广软件

vs2010新建立的mfc工程编译就出错error C2065: “DWORD_PTR”: 未声明的标识符0ys1234m2014.10.23浏览173次分享举报下面是报错内容:> stdafx.cpp1>1> NOTE: WINVER has been defined as 0x0500 or greater which enables1> Windows NT 5.0 and Windows 98 feature…...

dede 添加演示网站/怎么申请域名建立网站

目录&#xff08;1&#xff09;对于异步 I/O 操作的需求&#xff08;2&#xff09;异步 I/O API超时处理结果的顺序事件时间容错保证&#xff08;3&#xff09;代码实现&#xff08;1&#xff09;对于异步 I/O 操作的需求 在与外部系统交互&#xff08;用数据库中的数据扩充流…...