Centos 安装git
一般的平台通常都能通过自身的一个包管理工具去安装 git 的各个版本(包括当前官方最新版本),但是红帽系列通过 yum 安装却无法安装最新版本,而且都是很旧的版本,这种版本对许多新的命令以及特性都不支持,例如 switch 命令是 2.23 版本才发布的,在此之前的版本都没有,所以官方推荐像 CentOS 通过下载 git 源码包的方式去安装我们去阿里云下载git,找到版本,直接wget https://mirrors.edge.kernel.org/pub/software/scm/git/ 注意:直接 copy 下载地址,使用 wget 进行下载的时候,需要加上选项 --no-check-certificatewget --no-check-certificate https://mirrors.edge.kernel.org/pub/software/scm/git/git-2.9.5.tar.gz 接下来我们按照步骤进行安装就可以#安装前首先得安装依赖环境 yum install -y perl-devel #解压 tar -zxf git-2.9.5.tar.gz cd git-2.9.5 # 检查环境,配置安装路径 #编译安装 ./configure --prefix=/usr/local/git make && make install #添加到系统环境变量 vim ~/.bashrc #添加这一行 export PATH="/usr/local/git/bin:$PATH" #重载配置 source ~/.bashrc #查看版本 git version 就安装好了。
查看详情点赞评论收藏浏览382023-06-27 16:36:25Shell 基础命令语法
因为工作要用到shell自动部署一个项目,所以今天我们先来研究一下shell的基础命令。#!/bin/bash # 上面这个shell脚本的头,每门语言都有自己的头字符串echo "hello" echo "hi" # 变量 num="我是变量" echo $num num1=10 num2=50 #env env是读取环境变量 # 读取进程 echo "进程号:$0" echo "进程号:$$" # ``这个符号执行变量 echo `date` # 转义字符 加上-e才行 echo -e "你好啊\n成都"判断和if条件语句# 判断 test $num1 -eq $num2 echo "相等:$?" # 条件判断 if [ $num1 = 10 ]; then echo "这是等于10" else echo "默认" fi文件夹和文件基础创建# 创建文件夹,文件系列 dirName="aaa" if [ -e $dirName ]; then echo "文件夹存在,即将进入文件夹" cd $dirName echo "创建一个文件名字叫 a.txt" touch a.txt else echo "文件夹不存在,创建该文件夹" mkdir $dirName echo "进入文件夹,然后创建文件a.txt" cd $dirName touch a.txt fi用户交互# 读取键盘输入的内容,用户交互 read -p "请输入文件夹的名字:" dirFileName if [ -e $dirFileName ]; then echo "存在该文件夹" else mkdir $dirFileName fi # 命令选择 read -p "请输入y创建文件夹,n直接退出:" yes if [ $yes = "y" ]; then mkdir haaha elif [ $yes = "n" ]; then echo "退出了" ficase语句# case语句 read -p "请输入yes/no:" choice case $choice in yes | y* | Y*) echo "输入了yes" ;; no | n* | N*) echo "输入了no" ;; *) echo "输入了其他" ;; esacfor循环# for循环 declare -i sum=0 declare -i i=0 for (( i=0; i<=100; i++ )) do sum=$sum+$i; done echo "sum=$sum" for i in 1 2 3 4 5 do sum=$sum+$i; done echo "sum=$sum" # 扫描文件 for fileName in `ls` do if [ -d $fileName ]; then echo "$fileName是文件夹" elif [ -f $fileName ]; then echo "$fileName是文件" fi done函数# 函数 function getMax() { if [ $1 -gt $2 ]; then return $1 else return $2 fi } read -p "请输入数值1:" data1 read -p "请输入数值2:" data2 # 函数调用 getMax $data1 $data2 echo "$data1和$data2的最大值为:$?"
查看详情点赞2评论收藏浏览742023-06-27 15:44:37GitHub Copilot --Your AI pair programmer
一、介绍 GitHub Copilot是一种基于人工智能的代码自动补全工具,由GitHub和OpenAI联合开发。它可以根据上下文和注释,生成代码片段和函数,帮助开发者快速编写代码。 GitHub Copilot是通过机器学习技术实现的。 它的工作原理是,通过分析开发者的代码库,学习代码的语法和结构,并生成与当前上下文相关的代码建议。它可以同时处理多种编程语言,包括Java、Python、JavaScript、TypeScript、Ruby、Golang等。开发者可以在编辑器中直接使用GitHub Copilot,它会自动提示代码片段和函数,大大提高了开发效率。二、GitHub Copilot的功能 - 代码自动生成 - 代码补全 - 代码提示三、如何使用GitHub Copilot 1. 访问 https://github.com/features/copilot/ ,安装GitHub Copilot:在VS Code编辑器中搜索安装GitHub Copilot插件,并按照提示进行安装,其他编辑器类似。 2. 配置编辑器:在安装完GitHub Copilot插件后,需要进行相关配置。你需要在VS Code的设置中,将“editor.suggestSelection”设置为“first”,以便在输入代码时,GitHub Copilot的代码建议优先显示。此外,你还可以根据需要进行其他相关设置。 3. 使用GitHub Copilot:在编辑器中输入代码时,GitHub Copilot会自动提示代码片段和函数,你可以选择使用或者忽略。如果你选择使用,GitHub Copilot会自动补全代码,并根据上下文和注释生成相应的代码。如果你不满意生成的代码,可以通过修改代码或者提供更多的上下文来改进生成结果。 四、GitHub Copilot限制 1. 代码生成的准确性:GitHub Copilot的代码生成基于机器学习,它需要大量的数据和算法模型来进行训练和优化。然而,由于训练数据的限制和算法模型的不完善,GitHub Copilot生成的代码可能存在一定的准确性问题,需要人工进行修正和调整。 2. 缺乏创造性:GitHub Copilot只能根据已有的代码片段和函数,生成与当前上下文相关的代码建议。它缺乏创造性和创新性,无法像人类开发者一样,通过思考和创造来解决复杂的问题。 3. 安全性问题:由于GitHub Copilot是基于机器学习的技术,它需要训练数据来进行学习和优化。然而,训练数据可能包含敏感信息或机密代码,如果不加以保护,可能会导致安全性问题。 4. 版权和知识产权问题:GitHub Copilot的代码生成可能涉及到版权和知识产权问题。如果生成的代码涉及到他人的版权或知识产权,可能会引发法律纠纷和争议。 5. 依赖于互联网:GitHub Copilot需要连接互联网才能进行学习和优化。如果没有良好的互联网连接,可能会影响其性能和准确性。 6. 收费,个人每月10美元,每年100美元的价格,对大多数人来说存在门槛。 总的来说,GitHub Copilot虽然是一种非常有前途的技术,但也存在一些限制和局限性。在使用GitHub Copilot时,需要注意这些限制和局限性,并进行必要的修正和调整,以确保生成的代码的准确性和安全性五、总结 1. 提高软件开发效率:GitHub Copilot可以根据上下文和注释,生成与当前上下文相关的代码建议,帮助开发者快速编写代码。这将大大提高软件开发的效率,减少开发者的重复劳动和错误率。 2. 降低软件开发门槛:GitHub Copilot可以自动生成代码,使得软件开发的门槛降低,即使是没有编程经验的人也可以快速编写代码。这将有助于推动软件开发的普及和发展。 3. 挑战传统软件开发模式:GitHub Copilot的出现挑战了传统的软件开发模式,即由开发者手动编写代码。未来,随着人工智能技术的不断发展,自动化代码生成的比例可能会越来越高,从而改变软件开发的方式和模式。 4. 面临法律和道德问题:GitHub Copilot的代码生成基于机器学习,可能会涉及到版权、知识产权等法律和道德问题。未来,相关法律和政策的制定和完善将对GitHub Copilot的发展产生重要影响。 总的来说,GitHub Copilot是一种具有潜在影响的技术,它将对软件开发产生重要影响。未来,随着人工智能技术的不断发展,GitHub Copilot的功能和应用场景可能会不断扩展,我们需要密切关注其发展趋势和影响。
查看详情点赞2评论1收藏1浏览972023-06-27 11:12:53RSA加密与签名有什么区别
区别:RSA加密:RSA密码体制是一种公钥密码体制,加密算法公开,以分配的密钥作为加密解密的关键。RSA签名:签名就是在这份资料后面增加一段强而有力的证明,以此证明这段信息的发布者和这段信息的有效性完整性。在使用RSA进行通讯的时候,一般是两者结合,即:加密>签名>解密>验签公钥加密、私钥解密、私钥签名、公钥验签。加密是可逆的,而签名是不可逆的我们可以对一份资料用公钥加密,再用私钥解密,但我们对这份资料进行签名则是不可逆的,因为哈希本身是不可逆的。加密和签名都涉及到了使用公钥加密,前者加密了信息,后者加密了信息的hash加密和签名都是为了安全性考虑,但略有不同。加密是为了防止信息被泄露,而签名是为了防止信息被篡改。为什么签名是对信息hash之后加密,而不是加密一些特定的字符?这是因为防止中间人尝试向私钥拥有者反复发送一些特定的字符,得到加密后的信息,达到破解或者伪造之类的目的。所以用私钥随便加密信息是不安全的。RSA的签名与加密一般用在需要非常安全的环境下,例如支付总结:签名的本质其实就是加密,但是由于签名无需还原成明文,因此可以在加密前进行哈希处理。所以签名其实就是哈希+加密,而验签就是哈希+解密+比较。签名过程:对明文做哈希,拼接头信息,用私钥进行加密,得到签名。验签过程:用公钥解密签名,然后去除头信息,对明文做哈希,比较2段哈希值是否相同,相同则验签成功。
查看详情点赞1评论收藏浏览652023-06-27 10:07:20Gin框架系列教程(3)- go-mod包管理
(从 0-1 搭建你的Gin框架后台系统)go版本>=1.11一、什么是module?go中包管理工具二、使用module和不使用的区别使用环境变量中的GO111MODULE控制是否使用mod1.开启mod:go env -w GO111MODULE=on,会将包下载到gopath下的pkg下的mod文件夹中2.关闭mod:go env -w GO111MODULE=off,会将包下载到gopath下的src下3.go env GO111MODULE=auto,只有当当前目录在GOPATH/src目录之外而且当前目录包含go.mod文件或者其子目录包含go.mod文件才会启用。项目可以不用建在src下了,任何非中文路径下都可以,建议有个统一的代码路径三、go.mod文件的语法介绍go help go.mod 查看帮助示例:module my/thing go 1.13.4 require ( new/thing v2.3.4 old/thing v1.2.3 ) 1.module:指明根目录2.go 后面跟版本号是指定go的版本2.require是个动作指令,对依赖包起作用,比如require(依赖),还有exclude(排除),replace(替代),相同动作的可以放在一个动词+括号组成的结构中,如下:require ( new/thing v2.3.4 old/thing v1.2.3 ) require new/thing v2.3.4 require old/thing v1.2.3 // 排除 exclude old/thing v1.2.3 // 替换,使用箭头后的替换前面的 replace bad/thing v1.4.5 => good/thing v1.4.5 注意:exclude和replace仅适用于主的go.mod文件中,其他的依赖中会被忽略、可以使用replace替换无法获取的库,3.注释:使用//,没有/* xxx */这种块注释四、go mod 命令go mod help 查看帮助download 下载模块到本地缓存,go env中的GOCACHE路径,可以通过go clean -cache清空缓存 多个项目可以共享缓存的包 edit 在工具或脚本中编辑go.mod文件 graph 打印模块需求图 init 在当前目录下初始化新的模块 go mod init 【项目名】 默认使用当前路径的项目名称 tidy 添加缺失的模块以及移除无用的模块,生成go.sum文件 vendor 会自动下载项目中依赖的包到项目根目录下的vendor文件夹下,并写入go.mod文件,同时生成 modules.txt文件 go mod vender -v verify 检查当前模块的依赖是否全部下载下来,是否下载下来被修改过 why 解释为什么需要包或模块 注意:-v参数可以查看执行的详细信息 已经完成的项目可以这样操作来使用mod项目路径下执行go mod init然后再执行go mod vendor(或者直接运行项目)项目中可以是这样的执行顺序:init初始化 --> tidy 增删模块--> verify 校验模块-->vendor注意:项目中引入该项目下的任何路径都要是绝对路径,也就是以改项目名开头的路径使用mod的步骤:1.开启mod:go111module=on2.进入项目,执行go mod init (在项目根目录生成go.mod文件)3.启动项目(go.mod添加依赖的包)
查看详情点赞评论收藏浏览572023-06-19 16:53:29Gin框架系列教程(2)- Gin的第一次,hello world!
(从 0-1 搭建你的Gin框架后台系统)我们先新建一个文件,gin_demo,然后初始化一个go项目,我们用go mod来管理包,之后我们讲一下什么是modgo mod init gin_demo接下来新建main.go文件,引入Gin,开始第一个项目package main import "github.com/gin-gonic/gin" func main() { // g:=gin.New() g := gin.Default() g.GET("/", func(c *gin.Context) { c.String(200, "hello world") }) g.Run(":9000") }运行项目:go run main我们的第一个Gin项目就可以了。运行原理一、router:=gin.Default()初始化一个引擎,是gin.New()的升级二、router.GET1.RESTFUL风格的请求方法(method)2.有两个参数:relativePath:路由,string类型HandlerFunc:执行的函数3.可以使用router.Handle代替,多了个的method参数(字符串),method参数必须是大写的,如:GET三、执行的函数1.必须有个参数是gin.Context指针类型的注意:context是gin的一个重要组成部分。用来在中间层传递数据流。2.函数是个参数,不能调用四、router.Run启动http监听,有个address参数,字符串类型的,可以指定host和port注意:addr的host和port是用冒号分隔的只指定port,port前面必须要有冒号指定了host和port,host和port中间有冒号不能只指定hostg.Run(":9000")
查看详情点赞评论收藏浏览522023-06-19 16:49:00Gin框架系列教程(1)- gin框架介绍,环境搭建
(从 0-1 搭建你的Gin框架后台系统)一、gin框架介绍Gin 是一个用 Go (Golang) 编写的 web 框架。它是一个类似于 martini 但拥有更好性能的 API 框架,由于 httprouter,速度提高了近 40 倍,简单易用,是一个轻量级框架。如果你是性能和高效的追求者,你会爱上 Gin。二、为什么选择gin1.运行响应非常快2.快速开发3.文档齐全4.社区活跃三、特性1.快速:基于 Radix 树的路由,小内存占用。没有反射。可预测的 API 性能。2.支持中间件:传入的 HTTP 请求可以由一系列中间件和最终操作来处理。 例如:Logger,Authorization,GZIP,最终操作 DB。3.Crash 处理:Gin 可以 catch 一个发生在 HTTP 请求中的 panic 并 recover 它。这样,你的服务器将始终可用。例如,你可以向 Sentry 报告这个 panic!4.JSON 验证:Gin 可以解析并验证请求的 JSON,例如检查所需值的存在。5.路由组:更好地组织路由。是否需要授权,不同的 API 版本…… 此外,这些组可以无限制地嵌套而不会降低性能。6.错误管理:Gin 提供了一种方便的方法来收集 HTTP 请求期间发生的所有错误。最终,中间件可以将它们写入日志文件,数据库并通过网络发送。7.内置渲染:Gin 为 JSON,XML 和 HTML 渲染提供了易于使用的 API。8.可扩展性:新建一个中间件非常简单gin环境搭建注意:go1.9版本以上,很快将不再支持go1.7或go1.8。一、go环境安装1.go安装下载地址: https://go.dev/dl/这里选择你需要下载的版本,现在最新版是1.20.5,可以自己选择需要的版本。windows,和linux的安装方法稍有差异,不过大同小异,网上有很多方法,不过多介绍,可以多参考网上。这里我们直接安装windows版本(go1.19.4)。然后,我们安装windows软件一样,傻瓜式操作,直接下一步,下一步到底。2.环境配置:变量名:GOPATH 变量值:E:\go\workspace 你的工作路径变量名:GOROOT 变量值:E:\go\install go的安装路径变量名:Path 增加值:%GOROOT%\bin;%GOPATH%\bin;注意:删除自动添加的gopath和goroot注意添加完GOPATH,GOROOT两个变量值,还需要添加他们下面的bin执行目录。3.检查配置是否成功go env 查看GOPATH和GOROOT是否正确然后就可以看到安装的目录和版本是否正确,当显示下面截图,我们就安装成功了。二、工程管理:工作目录下新建三个文件夹1.src:用于以代码包的形式组织并保存go源码文件,2.pkg:用于存放经由go install命令构建的安装后的代码包,不需要手动创建3.bin:与pkg目录类似,在通过go install命令完成安装后,保存由go命令源码间生成的可执行文件三、安装gingin的安装就很简单了,直接命令拉取。go get -u github.com/gin-gonic/gin五、安装编辑工具(goland,vscode等都可以,看自己喜欢)第一节我们就到这里结束了,接下来我们就开始gin框架吧。hello world
查看详情点赞评论收藏浏览822023-06-19 15:36:49JS 生成二维码
JS生成二维码,其实很容易。下面我们来测试一下,一些简单的二维码就用JS生成。我们用一个生成二维码的库: https://github.com/SumiMakito/Awesome-qr.js 在网上翻了一些资料,这个库是没有什么缺点的,当然也可以尝试用其他生成二维码的库。html引入Awesome-qr库:<script src='./qr/dist/awesome-qr.js' type="text/javascript"></script> <script src="http://www.alingfeng.cn/js/jquery.min.js"></script>生成二维码:<img id="qrcodeimg"/ > <script> var creatqrcode = function (text, logo, background) { // 二维码生成参数 var text = "http://www.alingfeng.cn"; var size = 200; var colorDark = "#000000"; var margin = 9 var background = background || "#ffffff"; var logo = logo || ""; $("#qrcodeimg").css({width:size+"px",height:size+"px"}) new AwesomeQR.AwesomeQR({ text: text, // 内容 size: size, // 二维码大小 margin: margin, // 二维码白边大小 colorDark:colorDark, // 二维码颜色 colorLight: "rgba(159,255,255,0)", // 二维码背景颜色 logoImage : logo, // 二维码中间logo logoScale : 0.3, // 二维码中间logo大小 logoCornerRadius : 0, // 二维码中间logo圆角 }).draw() .then((dataURL) => { $("#qrcodeimg").attr("src", dataURL) }) .catch((err) => { console.error(err); }); } $(function(){ // 初始化生成二维码 creatqrcode("http://www.alingfeng.cn") }) </script>然后生成二维码试试:
查看详情点赞1评论收藏浏览1012023-06-17 10:56:44学习node+websocket+vue3搭建多人聊天室
跟着做小白都能做出来~~首先搭建前端Vite 项目兼容性注意Vite 需要 Node.js 版本 14.18+,16+。然而,有些模板需要依赖更高的 Node 版本才能正常运行,当你的包管理器发出警告时,请注意升级你的 Node 版本。使用 npm或yarn、pnpm任意安装,个人推荐pnpm,原因如下快速:pnpm比传统方案(yarn, npm)安装包的速度快了两倍,甚至比yarn2,pnp模式还要快更严格高效:node_modules 中的文件是从一个单一的可内容寻址的存储中链接过来的,代码无法访问任意包monorepo:天然内置支持当仓库多包$ npm create vite@latest $ yarn create vite $ pnpm create vite1.安装并输入项目名称2.这里选择vue3.根据个人选择,这里选择js4.到这里就成功快速搭建完成vite3+vue3前端基础框架了,跟着下面的命令执行即可在vsocde编辑器中打开打开src目录,建立以下模块结构首先cofigjs这里是用来统一管理地址的,后期维护也方便只需要修改地址即可1.congfig-index.js中,这里写的是websokct// 域名地址(项目实地址)这里写的是本地的地址 const BASE_URL='127.0.0.1' // 端口号 const WS_PORT='8000' // WebSocket协议的URL export const WS_ADDRESS=`ws://${BASE_URL}:${WS_PORT}`这里的hooks是vue3借鉴的react函数式组件的优点2.hooks-index.js// 这里的index.js文件用于统一引用导出hooks的方法。更规范 import useWebSocket from './websocket' export { useWebSocket }hooks-websocket.js// 引用config-index.js封装好的WebSocket协议的URL import { WS_ADDRESS } from "../configs" // 封装一个使用websocket的高阶函数 // 这里的handleMessage用于接收及发送的消息 function useWebSocket(handleMessage) { // 建立连接(创建WebSocket对象): // const ws = new WebSocket(url, [protocol]);// url:服务器端地址;protocol:可选,指定可接受的子协议。 const ws = new WebSocket(WS_ADDRESS) const init = () => { bindEvent() } init() function bindEvent() { // ws.onopen():连接建立时触发的事件 ws.addEventListener('open', handleOpen, false); // ws.onclose():关闭连接方法 ws.addEventListener('close', handleClose, false); // ws.onerror():通信发生错误时触发 ws.addEventListener('error', handleError, false); // ws.onmessage(): 客户端接收服务器端发送的信息时触发 ws.addEventListener('message', handleMessage, false); } function handleOpen(e) { console.log('WebSocket open', e) } function handleClose(e) { console.log('WebSocket close', e) } function handleError(e) { console.log('WebSocket error', e) } return ws } export default useWebSocket3.pages-home.vue建立简单的聊天页面<script setup> // onMounted:挂载完数据 // reactive:响应式数据,reactive 参数必须是对象 (json / arr) // toRaw:响应式转化普通对象 // toRefs:降reactive中的响应式结构为一个个的ref响应式数据 import { onMounted, reactive, toRaw, toRefs } from "vue"; // 引用全局路由对象useRouter import { useRouter } from "vue-router"; // 引用hooks中封装好的高阶函数,用于消息发送 import { useWebSocket } from '../hooks' // 这里useWebSocket(handleMessage)用于接收消息 const ws = useWebSocket(handleMessage) const router = useRouter() // 定义state用于接收和发送的消息 const state = reactive({ msg: '', msgList: [] }) // 结构state const { msg, msgList } = toRefs(state) // 获取用户名,没有就跳转到登录页 let username = '' onMounted(() => { username = localStorage.getItem('username') if (!username) { router.push('/login') return } }) // 发送消息按钮 const sendMsg = () => { if (msg.length < 1) { return } // ws.send():发送信息的方法 // 由于后台接收的是JSON类型,这里发送的消息需要JSON.stringify ws.send(JSON.stringify({ id: new Date().getTime(), user: localStorage.getItem('username'), dateTime: new Date().getTime(), // 这里必须让msg失去响应式或深拷贝该值,否则造成发送的消息根据再次输入的消息发生变更 msg: toRaw(msg.value), })) // 发送完清空消息框 msg.value = '' } // 用于接收发送过来的消息 function handleMessage(e) { // 由于后台接收的是JSON类型,这里发送的消息需要JSON.parse const msgData = JSON.parse(e.data) // 向列表添加每条聊天消息 msgList.value.push(msgData) } </script> <template> <div> <ul> <li v-for="item in msgList" :key="item.id"> <p>用户名:{{ item.user }}</p> <p>{{ new Date(item.dateTime) }}</p> <p>消息:{{ item.msg }}</p> </li> </ul> <input type="text" placeholder="请输入消息" v-model="msg"> <button @click="sendMsg">发送</button> </div> </template> <style scoped> li p { text-align: left; } </style> pages-login.vue进入聊天室的页面<template> <div> <input type="text" placeholder="请输入用户名" v-model="userName"> <button @click="enterSocket">进入聊天室</button> </div> </template> <script setup> import { onMounted, ref } from "vue"; import { useRouter} from 'vue-router' const router=useRouter() const userName=ref('') onMounted(()=>{ userName.value=localStorage.getItem('username') // 简单的判断是否已进入聊天室 if(userName.value){ router.push('/home') return } }) // 进入聊天室 const enterSocket=()=>{ if(userName.value<6){ alert('用户名不小于6位') return } localStorage.setItem('username',userName.value) userName.value='' router.push('/home') } </script>4.router-index.js 由于框架是没有自带router的这里需要去pnpm i vue-router安装下import { createRouter, createWebHashHistory } from "vue-router"; import routes from "./router"; const router = createRouter({ history: createWebHashHistory(), routes, }); export default router;router-router.jsimport login from "../pages/login.vue"; // 路由列表 let routes= [ { path: "/login", name: "login", component: login, }, { path: "/home", name: "home", component: () => import("../pages/home.vue"), children: [], }, ]; export default routes; 5.App.vue<template> <router-view></router-view> </template> 6.main.jsimport { createApp } from 'vue' import './style.css' import App from './App.vue' import router from "./router"; createApp(App).use(router).mount('#app') 6.在当前目录下新建server目录,用于node服务进入server目录生成package.json然后安装包在安装websocket的插件wx接着是node的目录如一下就完成在package.json中配置启动命令如index.js中写// 使用CommonJS require引入 // CommonJS 中的 require/exports 和 ES6 中的 import/export 区别 // CommonJS 模块是运行时加载,ES6 Modules 是编译时加载并输出接口。 // CommonJS 输出是值的拷贝;ES6 Modules输出的是值的引用,被输出模块的内部的改变会影响引用的改变。 // CommonJs 导入的模块路径可以是一个表达式,因为它使用的是 require() 方法,甚至这个表达式计算出来的内容是错误的路径,也可以通过编译到执行阶段再出错; // 而ES6 Modules 只能是字符串,并且路径不正确,编译阶段就会抛错。 // CommonJS this 指向当前模块,ES6 Modules this 指向 undefined // ES6 Modules 中没有这些顶层变量:arguments、require、module、exports、__filename、__dirname const Websocket=require('ws') ;((Ws)=>{ // 建立ws服务地址,这里跟前端都类似了唯一的区别是connection和message(连接和消息) const server=new Ws.Server({port:8000,host:"0.0.0.0"}) const init=()=>{ bindEvent() } function bindEvent(){ server.on('open',function(event){ server.send('OpenBarScanner') }) server.on('close',handelClose) server.on('error',handelError) server.on('connection',handelConnection) } function handelOpen(){ console.log('handelOpen') } function handelClose(){ console.log('handelClose') } function handelError(){ console.log('handelError') } // 连接时提示消息并发送到客户端 function handelConnection(ws){ console.log('WebSocket Connection') ws.on('message',handelMessage) } // 用于接收发送的消息 function handelMessage(msg){ server.clients.forEach((c)=>{ // 由于node发送到客户端的消息是Blob类型,这里需要用Buffer.form().toString()转化为utf8类型识别消息 c.send(Buffer.from(msg).toString('utf8')) }) } init() })(Websocket)
查看详情点赞2评论1收藏1浏览832023-06-15 15:25:03go 8个常用排序测试(冒泡排序,插入排序,选择排序,希尔排序,堆排序,基数排序,快速排序,计数排序)
前两天在做项目的时候,用到了排序,在网上查阅了一些资料做了部分对比,所以对常用的排序做了一些对比。当然不是全部,例如归并排序,二叉树之类的。等以后有时间可以了解一下。下面我们就了解一下这些排序;首先,我们先生成100位随机数字,然后对这一组数字进行排序。package main import ( "fmt" "math" mathRand "math/rand" "time" ) func main() { // 生成随机数 mathRand.Seed(time.Now().UnixNano()) var arr [100]int // 不重复生成 for i := 0; i < 100; i++ { arr[i] = mathRand.Intn(100) loop: for k := 0; k < i; k++ { if arr[k] == arr[i] { arr[i] = mathRand.Intn(100) goto loop } } } //打印随机数据 fmt.Println("生成的随机数切片:\n", arr) }1、冒泡排序// 冒泡排序方法 func mpArr(data [100]int) [100]int { var arrx int for i := 0; i < len(data)-1; i++ { for j := 0; j < len(data)-i-1; j++ { arrx++ if data[j] > data[j+1] { //交换 data[j], data[j+1] = data[j+1], data[j] } } } fmt.Println("冒泡排序排序次数", arrx) return data } mpArr := mpArr(arr) fmt.Println("冒泡排序之后的切片", mpArr)我们可以看到冒泡排序是比较稳定的。2、插入排序// 插入排序 func insertSort(data [100]int) [100]int { var j int var pxNum int for i := 1; i < len(data); i++ { temp := data[i] for j = i; j > 0 && temp < data[j-1]; j-- { data[j] = data[j-1] pxNum++ } data[j] = temp } fmt.Println("改进版插入排序次数:", pxNum) return data } // 改进版插入排序 func InsertionSort2(arr [100]int) [100]int { n := len(arr) var pxNum int for i := 1; i < n; i++ { // 无序区 tmp := arr[i] left, right := 0, i-1 for left <= right { pxNum++ mid := (left + right) / 2 if arr[mid] > tmp { right = mid - 1 } else { left = mid + 1 } } j := i - 1 for ; j >= left; j-- { // 有序区 arr[j+1] = arr[j] pxNum++ } arr[left] = tmp } fmt.Println("插入排序次数:", pxNum) return arr } // 使用插入排序 cr2Arr := InsertionSort2(arr) fmt.Println("插入排序之后的切片", cr2Arr) // 使用改进版插入排序 crArr := insertSort(arr) fmt.Println("改进版插入排序之后的切片", crArr)我们可以看到插入排序也是比较稳定的。3、选择排序// 选择排序 func SelectionSort(arr [100]int) [100]int { var pxNum int n := len(arr) for i := 0; i < n-1; i++ { minNumIndex := i // 无序区第一个 for j := i + 1; j < n; j++ { if arr[j] < arr[minNumIndex] { minNumIndex = j pxNum++ } } arr[i], arr[minNumIndex] = arr[minNumIndex], arr[i] } fmt.Println("选择排序次数:", pxNum) return arr } // 使用选择排序 xzArr := SelectionSort(arr) fmt.Println("选择排序之后的切片", xzArr)由此可以看出,选择排序是不稳定的4、希尔排序// 希尔排序 func shellSort(data [100]int) [100]int { var j int var pxNum int for h := len(data) / 2; h > 0; h /= 2 { //外层循环控制步长 for i := h; i < len(data); i++ { //内层循环是对步长个子切片做插入排序 temp := data[i] for j = i; j >= h && temp < data[j-h]; j -= h { data[j] = data[j-h] pxNum++ } data[j] = temp } } fmt.Println("希尔排序次数:", pxNum) return data } xrArr := shellSort(arr) fmt.Println("希尔排序之后的切片", xrArr)希尔排序也是不稳定的5、堆排序// 堆排序 func heapSort(data [100]int) [100]int { m := len(data) var pxNum int s := m / 2 for i := s; i >= 0; i-- { heap(data, i, m-1) } for i := m - 1; i > 0; i-- { data[i], data[0] = data[0], data[i] heap(data, 0, i-1) pxNum++ } fmt.Println("堆排序次数:", pxNum) return data } dArr := heapSort(arr) fmt.Println("堆排序之后的切片", dArr)堆排序也是不稳定的6、基数排序// 基数排序 func RadixSort(arr [100]int) [100]int { var pxNum int maxn := maxBitNum(arr) // arr最大位数 dev := 1 // 除数,保证商最后一位是我们想要的 mod := 10 // 模,取商的最后一位 for i := 0; i < maxn; i++ { // 进行maxn次排序 bucket := make([][]int, 10) // 定义10个空桶 result := make([]int, 0) // 存储中间结果 for _, v := range arr { n := v / dev % mod // 取出对应位的值,放入对应桶中 bucket[n] = append(bucket[n], v) pxNum++ } dev *= 10 // 按顺序存入中间切片 for j := 0; j < 10; j++ { result = append(result, bucket[j]...) pxNum++ } // 转存到原切片(结果) for k := range arr { arr[k] = result[k] pxNum++ } } fmt.Println("基数排序次数:", pxNum) return arr } zsArr := RadixSort(arr) fmt.Println("基数排序之后的切片", zsArr)基数排序是稳定的。7、快速排序// 快速排序 func QuickSort(left int, right int, array *[100]int) { l := left r := right // pivot 是中轴, 支点 pivot := array[(left+right)/2] temp := 0 // for 循环的目标是将比 pivot 小的数放到左边,比 pivot 大的数放到右边 for l < r { // 从 pivot 的左边找到大于等于pivot的值 for array[l] < pivot { l++ } // 从 pivot 的右边边找到小于等于pivot的值 for array[r] > pivot { r-- } // 1 >= r 表明本次分解任务完成, break if l >= r { break } // 交换 temp = array[l] array[l] = array[r] array[r] = temp // 优化 if array[l] == pivot { r-- } if array[r] == pivot { l++ } } // 如果 1== r, 再移动下 if l == r { l++ r-- } // 向左递归 if left < r { QuickSort(left, r, array) } // 向右递归 if right > l { QuickSort(l, right, array) } } ksArr := arr QuickSort(0, len(ksArr)-1, &ksArr) fmt.Println("快速排序之后的切片", ksArr)快排也是不稳定的。8、计数排序// 计数排序 func CountingSort(arr [100]int) [100]int { var pxNum int length := len(arr) maxValue := getMaxValue(arr) bucketLen := maxValue + 1 bucket := make([]int, bucketLen) sortedIndex := 0 // 统计每个元素出现的个数 for i := 0; i < length; i++ { bucket[arr[i]] += 1 pxNum++ } // 按照统计结果写入arr for j := 0; j < length; j++ { for bucket[j] > 0 { pxNum++ arr[sortedIndex] = j // bucket[j]的值是统计结果,后面会变化,j是真正值 sortedIndex++ bucket[j]-- } } fmt.Println("计数排序次数:", pxNum) return arr } jsArr := CountingSort(arr) fmt.Println("计数排序之后的切片", jsArr)计数排序也是稳定的 稳定的排序算法有:冒泡排序、插入排序、计数排序、基数排序。 不稳定的排序算法有:选择排序、希尔排序、快速排序、堆排序。 当然每个排序方法只有最适合的场景,不能单从稳定性考虑,也要从性能上考虑,不能一概而论。
查看详情点赞1评论收藏浏览762023-06-14 09:58:31