gin框架教程笔记

参考

官方中文文档:https://gin-gonic.com/zh-cn/docs/introduction/ 但是示例截图少
https://www.kancloud.cn/shuangdeyu/gin_book/949411
https://www.topgoer.com/gin%E6%A1%86%E6%9E%B6/ 这个网站不光有gin框架 适合阅读

吉米老师的 :https://www.liwenzhou.com/posts/Go/Gin_framework/
他的其他链接:https://www.liwenzhou.com/posts/Go/golang-menu/
docker部署go项目:https://www.liwenzhou.com/posts/Go/how_to_deploy_go_app_using_docker/

官方网站:https://gin-gonic.github.io/gin/
Github地址:https://github.com/gin-gonic/gin

什么是框架

框架是指半成品的应用,一般需要填充少许代码或者无需填充代码就可以运行,只是这样的应用缺少业务逻辑。

我们使用框架开发,主要工作就是在框架上补充业务逻辑代码。所以,借助框架进行开发,不仅可以减少开发时间、提高效率,也有助于团队统一编码风格,形成 编程规范。

gin 框架是一个 Web 框架,它封装了 路由、Cookie、Session、参数处理、数据编解码以及中间件等功能,简单高效,降低了开发 Web 应用的难度。

gin 是一个使用 Go 语言编写的 Web 后端框架,具有简洁、轻量、支持高并发、封装优雅、API 友好、快速灵活、容错方便等特点。

gin 和 beego 是 Go 语言编写 Web 应用最常用的后端框架。

使用 Go 语言开发 Web 应用,对于框架的依赖要比其它编程语言要小。Go 语言内置的 net/http 包简单轻便,性能优良。而且,大多数的 Go 语言 Web 框架都是在其之上进行的封装。

安装与导入

$ go get -u github.com/gin-gonic/gin
import (
	"net/http" //通常还需要导入 net/http 包,因为代码中一般需要一些常量,比如返回码 http.StatusOK。
	"github.com/gin-gonic/gin"
)

在这里插入图片描述

学学这个排版。

Hello World

package main

import (
    "net/http"
    "github.com/gin-gonic/gin"
)

func main() {
   // 1.创建路由
   engine := gin.Default()
   
   // 2.绑定路由规则,执行的函数,这里写成了匿名函数的形式 也可单独写一个函数
   // gin.Context,封装了request和response
   //指定对什么网址进行相应,响应什么内容
   engine.GET("/", func(c *gin.Context) {
      c.String(http.StatusOK, "Hello World!")
   })
   
   // 3.监听端口,默认在8080
   engine.Run()//相当于 engine.Run(":8080")
}

上面GET方法可拆为:

helloHandler := func(context *gin.Context) {
	 context.String(http.StatusOK, "Hello World!")
}
func main() {
	//···省略
	engine.GET("/",helloHandler)
}

//GET is a shortcut for router.Handle("GET", path, handle)
helloHandler := func(context *gin.Context) {
	fmt.Fprint(context.Writer, "Hello World!")
}
r.Handle("GET", "/hello", helloHandler)

打开浏览器,输入 http://localhost:8080,就可以看到浏览器输出:

Hello World!

如果我们不使用gin框架,原生的go也可以实现:
在这里插入图片描述

路由处理

什么是路由

在web开发中,“route”是指根据url分配到对应的处理程序。

路由(route)就是根据 HTTP 请求的 URL,设置由哪个函数来处理请求。路由是 Web 框架的核心功能。

路由通常这样实现:根据路由里的字符 “/”,把路由切分成多个字符串数组,然后构造成树状结构;寻址的时候,先把请求的 URL 按照 “/” 进行切分,然后遍历树进行寻址。

比如:定义了两个路由 /user/get,/user/delete,则会构造出拥有三个节点的路由树,根节点是 user,两个子节点分别是 get 和 delete。

gin 框架路由库

gin 框架中采用的路由库是基于 httprouter 开发的。
httprouter 项目地址:https://github.com/julienschmidt/httprouter。

gin 框架路由的范例

package main

import (
    "net/http"
    "github.com/gin-gonic/gin"
)

func main() {
    engine := gin.Default()

    // 下面是两条路由
    engine.GET("/", func(c *gin.Context) {
        c.String(http.StatusOK, "编程教程")
    })

    engine.GET("/hello", func(c *gin.Context) {
        c.String(http.StatusOK, "Hello World")
    })
    // 监听端口默认为8080
    engine.Run(":8080")
}

运行程序,然后打开浏览器,输入 http://localhost:8080/,可以看到浏览器输出:编程教程。然后输入 http://localhost:8080/hello,可以看到浏览器输出:Hello World。

r.GET("/a",func(c *gin.Context) {})
r.POST("/b",func(c *gin.Context) {})
//此外,还有一个可以匹配所有请求方法的Any方法如下
r.Any("/c",func(c *gin.Context) {})

RESTAPI

web 应用程序,一般分为前端和后端两个部分。前后端通信通常需要一种统一机制,以方便不同的前端设备与后端进行通信。这种需求导致了 API 构架的流行,甚至出现 “API First” 的设计思想。

RESTful API 是目前比较成熟的一套 web 应用程序的 API 设计理论,用于 web 前后端的数据交互。

RESTful API 路径

RESTful API 的路径又称 “终点”(endpoint),表示 API 的具体网址。

在 RESTful 架构中,每个网址代表一种资源(resource),比如:有一个 API 提供动物园(zoo)的信息,还包括各种动物和雇员的信息,则它的路径应该设计成下面这样:

https://api.example.com/v1/zoos
https://api.example.com/v1/animals
https://api.example.com/v1/employees

RESTful API HTTP 动词

在 RESTful API 设计理论中,对于资源的操作,由 HTTP 动词表示。

常用的 HTTP 动词有下面五个(括号里是对应的数据库 SQL 命令)。

  • GET(SELECT):从服务器取出资源(一项或多项)。
  • POST(CREATE):在服务器新建一个资源。
  • PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
  • PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
  • DELETE(DELETE):从服务器删除资源。

还有两个不常用的HTTP动词。

  • HEAD:获取资源的元数据。
  • OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。

比如

  • GET /zoos:列出所有动物园
  • POST /zoos:新建一个动物园
  • GET /zoos/ID:获取某个指定动物园的信息
  • PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
  • PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
  • DELETE /zoos/ID:删除某个动物园
  • GET /zoos/ID/animals:列出某个指定动物园的所有动物
  • DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物

下面是一些例子

gin 框架支持 RESTful 风格的 API。

RESTful 即 Representational State Transfer 的缩写。直接翻译的意思是 “表现层状态转化”,是一种互联网应用程序的API设计理念:URL 定位资源,用 HTTP 方法描述操作,例如:

  1. 获取文章 /blog/getXxx Get blog/Xxx
  2. 添加文章 /blog/addXxx POST blog/Xxx
  3. 修改文章 /blog/updateXxx PUT blog/Xxx
  4. 删除文章 /blog/delXxxx DELETE blog/Xxx

在这里插入图片描述

gin 框架路由的基本语法

Gin 的路由支持 HTTP 的 GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS 方法的请求,同时还有一个 Any 函数,可以同时支持以上的所有请求。

Gin 的路由通常的使用方法如下:

// 获取默认的 gin Engine,Engine 中包含了所有路由处理的接口
engine := gin.Default()

// Get 请求路由
engine.GET("/", func(context *gin.Context) {
    context.String(http.StatusOK, "hello gin get method")
})
// Post 请求路由
engine.POST("/", func(context *gin.Context) {
    context.String(http.StatusOK, "hello gin post method")
})
// Put 请求路由 
engine.PUT("/", func(context *gin.Context) {
    context.String(http.StatusOK, "hello gin put method")
})
// Delete 请求路由
engine.DELETE("/", func(context *gin.Context) {
    context.String(http.StatusOK, "hello gin delete method")
})
// Patch 请求路由
engine.PATCH("/", func(context *gin.Context) {
    context.String(http.StatusOK, "hello gin patch method")
})
// Head 请求路由
engine.HEAD("/", func(context *gin.Context) {
    context.String(http.StatusOK, "hello gin head method")
})
// Options 请求路由
engine.OPTIONS("/", func(context *gin.Context) {
    context.String(http.StatusOK, "hello gin options method")
})

gin路由分组

我们在使用 web 框架开发时,经常会根据业务逻辑给一个模块划分一组路由。

把一个模块相关的方法都写在一个路由下,主要好处是业务逻辑清晰,便于管理和查找相关的代码。

例如:goods 为商品模块,我们规划它的操作路由。

/goods/addGoods 添加商品
/goods/delGoods 删除商品

gin 框架支持路由分组(routes group),路由分组的关键词为 group。

engine.Group("/groupname")

写法1

package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
)

func loginEndpoint(c *gin.Context){
    fmt.Println("这是login方法")
}

func submitEndpoint(c *gin.Context){
    fmt.Println("这是submit方法")
}

func readEndpoint(c *gin.Context){
    fmt.Println("这是read方法")
}

func main() {
    engine := gin.Default()

    //v1组路由     
    // {} 是书写规范  这个语法是咋实现的?  是函数先返回一个结构体 再实例化的意思么?
    v1 := engine.Group("/v1")
    {
        v1.GET("/login", loginEndpoint)
        v1.GET("/submit", submitEndpoint)
        v1.GET("/read", readEndpoint)
    }

    //v2组路由
    v2: = engine.Group("/v2")
    {
        v2.GET("/login", loginEndpoint)
        v2.GET("/submit", submitEndpoint)
        v2.GET("/read", readEndpoint)
    }
    engine.Run()
}

打开浏览器,输入 http://localhost:8080,分别访问:

http://localhost:8080/v1/login
http://localhost:8080/v1/submit
http://localhost:8080/v1/read
http://localhost:8080/v2/login
http://localhost:8080/v2/submit
http://localhost:8080/v2/read

浏览器会输出对应的 API 内容。

写法2

也可以这样:

func main() {
	r := gin.Default()
	user := r.Group("/user")
	user.GET("/index", func(c *gin.Context) {})
	user.POST("/login", func(c *gin.Context) {})
	r.Run()
}

在这里插入图片描述
区别,就在于不用再单独写路由了

参数处理

web 程序中经常需要处理各种形式的参数,参数是处理 HTTP 请求中很重要的工作,它是前端向后端提交数据的基本形式。

gin 框架内置了处理 HTTP 各种参数的方法,包括 API 参数,URL 参数 以及 表单参数的处理。

query参数处理(URL 参数处理)(get)

例如​/name=admin&pwd=123456​,我们想得到name和pwd的值

URL 参数可以通过 DefaultQuery() 或 Query() 方法获取。
DefaultQuery() 若参数不存在,则返回默认值,Query()若不存在,返回空串。

package main

import (
	"net/http"
	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
	r.GET("/", func(c *gin.Context) {
		name := c.DefaultQuery("name", "admin")
		pwd := c.Query("pwd")
		// fmt.Printf("name:%s ; pwd:%s",name,pwd)
		c.JSON(http.StatusOK, gin.H{
			"name": name,
			"pwd":  pwd,
		})
	})
	r.Run()
}

表单Form参数处理 (post)

表单传输为post请求,http常见的传输格式为四种:

application/json
application/x-www-form-urlencoded
application/xml
multipart/form-data

表单参数可以通过 PostForm() 方法获取,该方法默认解析的是 x-www-form-urlencoded 或 from-data 格式的参数。

package main

import (
    "fmt"
    "net/http"
    "github.com/gin-gonic/gin"
)

func main() {
    engine := gin.Default()
    engine.POST("/form", func(c *gin.Context) {
        types := c.DefaultPostForm("type", "post")
        username := c.PostForm("username")
        password := c.PostForm("userpassword")
        c.String(http.StatusOK, fmt.Sprintf("username:%s,password:%s,type:%s", username, password, types))
    })
    engine.Run()
}

从表单中获取了 types、username、password 三个参数。

API 参数处理

gin 框架中,可以通过 Context 的 Param 方法来获取 API 参数。

比如:提取 http://localhost:8080/user/zhangsan 的参数 zhangsan。

package main

import (
    "net/http"
    "strings"

    "github.com/gin-gonic/gin"
)

func main() {
    engine := gin.Default()
    engine.GET("/user/:name", func(c *gin.Context) {
        name := c.Param("name")
        c.String(http.StatusOK, "name=" + name)
    })

    // 监听8080端口
    engine.Run(":8080")
}

运行程序,浏览器中输入:http://localhost:8080/user/zhangsan,浏览器会输出:name=zhangsan。

/user/:name/*action 这种也是api参数 后面会再讲

上传文件

简单来说 就是

FormFile("文件名")
<input type="file" name="file" >

上传单个文件

gin 框架中,multipart/form-data 格式用于文件上。

文件上传与原生的 net/http 方法类似,不同在于 gin 把原生的 request 封装到 c.Request 中。

前端 html 文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data">
          上传文件:
          <input type="file" name="file" >
          <input type="submit" value="提交">
    </form>
</body>
</html>

文件可以保存为 test.html。

后端 go 文件:

package main

import (
    "github.com/gin-gonic/gin"
)

func main() {
    engine := gin.Default()
    //限制上传最大尺寸
    engine.MaxMultipartMemory = 8 << 20
    engine.POST("/upload", func(c *gin.Context) {
        file, err := c.FormFile("file")
        if err != nil {
            c.String(500, "上传图片出错")
        }
        // c.JSON(200, gin.H{"message": file.Header.Context})
        c.SaveUploadedFile(file, file.Filename)
        c.String(http.StatusOK, file.Filename)
    })
    engine.Run()
}

运行程序后,浏览器访问 test.html文件,就可以在浏览器中上传文件。

OR

前端页面代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <title>上传文件示例</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
    <input type="file" name="f1">
    <input type="submit" value="上传">
</form>
</body>
</html>

后端Gin框架部分代码

package main

import (
	"fmt"
	"log"
	"net/http"
	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
	// 处理multipart forms提交文件时默认的内存限制是32 MiB
	// 可以通过下面的方式修改
	// r.MaxMultipartMemory = 8 << 20  // 8 MiB
	r.POST("/upload", func(c *gin.Context) {
		// 单个文件
		file, err := c.FormFile("f1")
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{
				"message": err.Error(),
			})
			return
		}

		log.Println(file.Filename)
		dst := fmt.Sprintf("C:/tmp/%s", file.Filename)
		// 上传文件到指定的目录
		c.SaveUploadedFile(file, dst)
		c.JSON(http.StatusOK, gin.H{
			"message": fmt.Sprintf("'%s' uploaded!", file.Filename),
		})
	})
	r.Run()
}

上传特定文件

有的用户上传文件需要限制上传文件的类型以及上传文件的大小,但是 gin 框架暂时没有这些函数。因此,我们基于原生的函数写了一个可以限制大小以及文件类型的上传函数。

package main

import (
    "fmt"
    "log"
    "net/http"
    "github.com/gin-gonic/gin"
)

func main() {
    engine := gin.Default()
    engine.POST("/upload", func(c *gin.Context) {
        _, headers, err := c.Request.FormFile("file")
        if err != nil {
            log.Printf("Error when try to get file: %v", err)
        }
        //headers.Size 获取文件大小
        if headers.Size > 1024*1024*2 {
            fmt.Println("文件太大了")
            return
        }
        //headers.Header.Get("Content-Type")获取上传文件的类型
        if headers.Header.Get("Content-Type") != "image/png" {
            fmt.Println("只允许上传png图片")
            return
        }
        c.SaveUploadedFile(headers, "./video/"+headers.Filename)
        c.String(http.StatusOK, headers.Filename)
    })
    engine.Run()
}

上传多个文件

前端 html 文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data">
          上传文件:
          <input type="file" name="files" multiple>
          <input type="submit" value="提交">
    </form>
</body>
</html>

文件可以保存为 test.html。

package main

import (
   "github.com/gin-gonic/gin"
   "net/http"
   "fmt"
)

func main() {
   engine := gin.Default()
   // 限制表单上传大小 8MB,默认为32MB
   engine.MaxMultipartMemory = 8 << 20
   engine.POST("/upload", func(c *gin.Context) {
      form, err := c.MultipartForm()
      if err != nil {
         c.String(http.StatusBadRequest, fmt.Sprintf("get err %s", err.Error()))
      }
      // 获取所有图片
      files := form.File["files"]
      // 遍历所有图片
      for _, file := range files {
         // 逐个保存
         if err := c.SaveUploadedFile(file, file.Filename); err != nil {
            c.String(http.StatusBadRequest, fmt.Sprintf("upload err %s", err.Error()))
            return
         }
      }
      c.String(200, fmt.Sprintf("upload ok %d files", len(files)))
   })
 
   engine.Run()
}

运行程序后,浏览器访问 test.html文件,就可以在浏览器中上传文件。

OR

package main

import (
	"fmt"
	"log"
	"net/http"
	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
	// 处理multipart forms提交文件时默认的内存限制是32 MiB
	// 可以通过下面的方式修改
	// r.MaxMultipartMemory = 8 << 20  // 8 MiB
	r.POST("/upload", func(c *gin.Context) {
		// Multipart form
		form, _ := c.MultipartForm()
		files := form.File["file"]

		for index, file := range files {
			log.Println(file.Filename)
			dst := fmt.Sprintf("C:/tmp/%s_%d", file.Filename, index)
			// 上传文件到指定的目录
			c.SaveUploadedFile(file, dst)
		}
		c.JSON(http.StatusOK, gin.H{
			"message": fmt.Sprintf("%d files uploaded!", len(files)),
		})
	})
	r.Run()
}

中间件

Gin框架允许开发者在处理请求的过程中,加入钩子函数,这个钩子函数就叫中间件。中间件适合处理一些公共的业务逻辑,比如登陆认证,权限校验,记录日志等。具体使用方法如下

package main

import (
	"fmt"
	"net/http"
	"time"
	"github.com/gin-gonic/gin"
)

//定义一个中间键m1统计请求处理函数耗时
func m1(c *gin.Context) {
	fmt.Println("m1 in...")
	start := time.Now()
	// c.Next() //调用后续的处理函数
	c.Abort()//阻止调用后续的处理函数
	cost := time.Since(start)
	fmt.Printf("cost:%v\n", cost)
}

func index(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"msg": "ok",
	})
}

func main() {
	r := gin.Default()
	r.GET("/", m1, index)  //之前没有中间件的写法r.GET("/", index)
	r.Run()
}

还没找到分类

func main() {
	r := gin.Default()

	// gin.H 是map[string]interface{}的缩写
	r.GET("/someJSON", func(c *gin.Context) {
		// 方式一:自己拼接JSON
		c.JSON(http.StatusOK, gin.H{"message": "Hello world!"})
	})
	r.GET("/moreJSON", func(c *gin.Context) {
		// 方法二:使用结构体
		var msg struct {
			Name    string `json:"user"`
			Message string
			Age     int
		}
		msg.Name = "小王子"
		msg.Message = "Hello world!"
		msg.Age = 18
		c.JSON(http.StatusOK, msg)
	})
	r.Run(":8080")
}

其中 方法一 等价
在这里插入图片描述
注意,结构体方法,变量名要大写。所以用tag

template

在一些前后端不分离的Web架构中,我们通常需要在后端将一些数据渲染到HTML文档中,从而实现动态的网页(网页的布局和样式大致一样,但展示的内容并不一样)效果。

我们这里说的模板可以理解为事先定义好的HTML文档文件,模板渲染的作用机制可以简单理解为文本替换操作–使用相应的数据去替换HTML文档中事先准备好的标记。

可以看这个:https://www.liwenzhou.com/posts/Go/go_template/

插件推荐

在这里插入图片描述

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

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

相关文章

Redis基本使用

Redis基本使用 1.通用命令2.基本数据类型2.1 String2.2 Hash2.3 List2.4 Set2.5 SortedSet 3. SpringDataRedis3.1 简介3.2 快速代码示例3.3 序列化 1.通用命令 针对所有数据类型的操作可以在Redis官方文档查看。以下是通用的命令。 KEYS&#xff1a;查看符合模板的所有key D…

气压计LPS25HB开发(1)----轮询获取气压计数据

气压计LPS25HB开发----1.轮询获取气压计数据 概述视频教学样品申请源码下载产品特性通信模式速率生成STM32CUBEMX串口配置IIC配置SA0地址设置串口重定向参考程序SA0设置模块地址获取ID复位操作BDU设置设置速率轮询读取数据演示 概述 本文将介绍如何使用 LPS25HB 传感器来读取数…

QT信号与槽实现方式

1、第一种实现方式 在QT开发工具UI界面先拖入按钮&#xff0c;然后鼠标右键拖入按钮&#xff0c;点击选中槽&#xff0c;在页面选着需要的信号&#xff0c;然后OK&#xff0c;随即将会跳转到类的.cpp文件&#xff0c;&#xff08;这种UI代码结合的方式&#xff0c;会自动去绑定…

【剪枝实战】使用VGGNet训练、稀疏训练、剪枝、微调等,剪枝出只有3M的模型

摘要 本次剪枝实战是基于下面这篇论文去复现的&#xff0c;主要是实现对BN层的γ/gamma进行剪枝操作&#xff0c;本文用到的代码和数据集都可以在我的资源中免费下载到。 相关论文&#xff1a;Learning Efficient Convolutional Networks through Network Slimming (ICCV 2017…

【小白学机器学习9】自己纯手动计算验证,EXCEL的一元线性回归的各种参数值

目录 0 目标 1 构造模型 1.1 构造模型的思路 1.2 具体模型构造的EXCEL公式和过程 2 直接用EXCEL画图&#xff0c;然后生成趋势线的方式进行回归分析 2.1 先选择“观测值Y”的数据&#xff0c;用散点图或者折线图作图 2.2 然后添加趋势线和设置趋势线格式 2.3 生成趋…

服务器Debian 12.x中安装Jupyer并配置远程访问

服务器系统&#xff1a;Debian 12.x&#xff1b;IP地址&#xff1a;10.100.2.138 客户端&#xff1a;Windows 10;IP地址&#xff1a;10.100.2.38 利用ssh登录服务器&#xff1a; 1.安装python3 #apt install python3 2.安装pip #apt install python3-pip … 3.安装virtualen…

HBase分布式数据库的原理和架构

一、HBase简介 HBase是是一个高性能、高可靠性、面向列的分布式数据库&#xff0c;它是为了在廉价的硬件集群上存储大规模数据而设计的。HBase利用Hadoop HDFS作为其文件存储系统&#xff0c;且Hbase是基于Zookeeper的。 二、HBase架构 *图片引用 Hbase采用Master/Slave架构…

PTA-练习1

目录 实验2-3-8 计算火车运行时间 实验2-4-4 求简单交错序列前N项和 实验2-4-5 输出华氏-摄氏温度转换表 实验3-4 统计字符[2] 实验3-5 查询水果价格 实验3-11 求一元二次方程的根 实验4-1-1 统计数字字符和空格 实验2-3-8 计算火车运行时间 时钟数有两种情况&#xff1…

使用BBDown下载bilibili视频的方法

一款命令行式哔哩哔哩下载器. Bilibili Downloader. 下载地址 https://github.com/nilaoda/BBDown 功能 番剧下载(Web|TV|App) 课程下载(Web) 普通内容下载(Web|TV|App) 合集/列表/收藏夹/个人空间解析 多分P自动下载 选择指定分P进行下载 选择指定清晰度进行下载 下载外挂字幕…

解决驱动开发中<stdlib.h> no such file 的问题

前言 在进行驱动开发时&#xff0c;需要使用malloc等函数&#xff0c;导入C库<stdlib.h>出现bug。 嵌入式驱动学习专栏将详细记录博主学习驱动的详细过程&#xff0c;未来预计四个月将高强度更新本专栏&#xff0c;喜欢的可以关注本博主并订阅本专栏&#xff0c;一起讨论…

java并发编程之 volatile关键字

1、简单介绍一下JMM Java 内存模型&#xff08;Java Memory Model 简称JMM&#xff09;是一种抽象的概念&#xff0c;并不真实存在&#xff0c;指一组规则或规范&#xff0c;通过这组规范定义了程序中各个变量的访问方式。java内存模型(JMM)屏蔽掉各种硬件和操作系统的内存访问…

OpenvSwitch VXLAN 隧道实验

OpenvSwitch VXLAN 隧道实验 最近在了解 openstack 网络&#xff0c;下面基于ubuntu虚拟机安装OpenvSwitch&#xff0c;测试vxlan的基本配置。 节点信息&#xff1a; 主机名IP地址OS网卡node1192.168.95.11Ubuntu 22.04ens33node2192.168.95.12Ubuntu 22.04ens33 网卡信息&…

XUbuntu22.04之关闭todesk开机自启动(二百二十一)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

Python数据分析-4

1.对于一组电影数据&#xff0c;呈现出rating,runtime的分布情况&#xff1a; #encodingutf-8 import pandas as pd import numpy as np from matplotlib import pyplot as plt file_path "./youtube_video_data/IMDB-Movie-Data.csv" df pd.read_csv(file_path) …

React低代码平台实战:构建高效、灵活的应用新范式

文章目录 每日一句正能量前言一、React与低代码平台的结合优势二、基于React的低代码平台开发挑战三、基于React的低代码平台开发实践后记好书推荐编辑推荐内容简介作者简介目录前言为什么要写这本书 读者对象如何阅读本书 赠书活动 每日一句正能量 人生之美&#xff0c;不在争…

C# 根据两点名称,寻找两短路程的最优解,【有数据库设计,完整代码】

前言 如果我们遇到路径问题&#xff0c;可以使用点点连线&#xff0c;给定一个点&#xff0c;可以到达另外几个点&#xff0c;寻找最优解 例&#xff1a;如下图所示&#xff0c;如果要从A1-C1,可以有三条路 1.A1-B1-C1 2.A1-B2-C1 3.A1-B3-C1 最优解肯定是A1-B1-C1&#xff0c…

15. jwt认证中间件

在上一篇登录功能的实现中&#xff0c;我们使用了jwt作为鉴权组件&#xff0c;其中登录后会颁发token。前端在访问后续请求时&#xff0c;可以带上这个token。对于一些需要权限校验的请求&#xff0c;我们就需要验证这个token&#xff0c;从token中获取到用户id&#xff08;有了…

Unity Timeline学习笔记(1) - 创建TL和添加动画片段

Timeline在刚出的时候学习了一下&#xff0c;但是因为一些原因一直都没用在工作中使用。 版本也迭代了很久不用都不会用了&#xff0c;抽时间回顾和复习一下&#xff0c;做一个笔记后面可以翻出来看。 创建Timeline 首先我们创建一个场景&#xff0c;放入一个Plane地板&#…

【机器学习智能硬件开发全解】(四)—— 政安晨:嵌入式系统基本素养【后摩尔时代】

随着物联网、大数据、人工智能时代的到来&#xff0c;海量的数据分析、大量复杂的运算对CPU的算力要求越来越高。 CPU内部的大部分资源用于缓存和逻辑控制&#xff0c;适合运行具有分支跳转、逻辑复杂、数据结构不规则、递归等特点的串行程序。 在集成电路工艺制程将要达到极…

PgSQL技术内幕 - 优化器如何估算行数

PgSQL技术内幕 - 优化器如何估算行数 PgSQL优化器根据统计信息估算执行计划路径的代价&#xff0c;从而选择出最优的执行计划。而这些统计信息来自pg_statistic&#xff0c;当然这个系统表是由ANALYZE或者VACUUM进行样本采集而来。关于该系统表的介绍详见&#xff1a;PgSQL技术…