Go红队开发—基础语法入门

news/2025/2/26 22:30:03

文章目录

  • 基础语法
    • 语法框架
    • 数据类型
    • 类型转换
    • 变量var定义
    • 常量
    • iota 枚举
    • 数组
      • 切片
    • 结构体
      • 结构体方法
    • 指针
    • map
    • 类型转换
    • 导入包
    • 字符串strings包
      • 字符拼接
      • Contains
      • Replace
      • 更多函数解释
    • 输入输出
      • 字符串格式化
      • fmt:Scanf、Scan、Scanln
        • Scanf
        • Scan
        • Scanln
      • fmt:Println、Print、Printf、Sprintf
        • Println
        • Print
        • Printf
        • Sprintf
    • 条件控制
      • if else
      • Switch
      • For
    • 函数
      • 格式
      • 可数的参数变量函数
      • 可变参数相同类型参数变量函数
      • 返回一个值
      • 返回多个值
      • 匿名函数
      • 初始化函数
      • 指定变量名作为返回值
    • 重载方法Methods
      • ⽅法表达式
    • defer(很重要,对理解通道和遍历通道)
  • 基础语法阶段练习
    • 简单编码解码器
    • 简单图书馆

基础语法

语法框架

package main
import "fmt"
func main() {
	fmt.Println("Hello Go!") 
}

基本使用,首先学会打印,目前介绍两个即可

println("hello word")
fmt.Println("hello word")  //这个属于fmt包的,使用的时候需要引入import "fmt"

数据类型

单引号:rune类型
双引号:string类型
数字型:int , uint uint32
bool型:true , false

类型转换

//相同类型转换 ,比如整数类型,直接
var int_num int = 111
var f64_num float64 = float64(int_num)


//不同类型的转换
//借助"strconv"包
//int转string
str1 := strconv.Itoa(int_num)

//以后遇到了再慢慢补充

变量var定义

//第一种,比较完整的定义与赋值
var a int
var a int = 123

//简短声明,这种就是忽略了你var作为变量的意思还有去掉了类型,直接通过值来自动帮你搞定
a := 123

//多个变量声明,这个和python无二
#正常声明
var a,b int
#正常声明加赋值
var a,b int = 1,2
#简短多变量
a,b := 1,2


//匿名变量
#首先说明一下:在go中,如果你定义了变量不使用他会报错,所以匿名变量就出现了,让你定义暂存可以不用
_,a := 1,2

_下划线匿名变量,存储起来。
在go中,如果你定义了变量而不用就会报错,所以匿名变量存储是一个比较好的解决方式。

//比如当你使用for r循环的时候会有一个下标需要你接受,但是你又不想用他
for _, value := range arr{
    fmt.Println(value)
}

常量

常量用const,且不受使用影响的,定义了可以不使用

const S int = 123 //常规定义常量

const S = 123  //可以不用指定类型,会隐式指定合适的类型

//定义多个常量,使用括号包裹
const (
    a = 1
    s = "s"
    b = true
)

iota 枚举

const (
		aas = iota //指定变量名等于iota后,往后的变量都不用给值,变量名随意,默认这里从0开始
		bbs
		ccs
		dds
)
fmt.Println("这里是iota的常量值:")
fmt.Println(aas, bbs, ccs, dds)

image-20241111124516295

数组

在go中你会发现啥都反过来定义了,包括数组。

var arr  = [len]int{元素1,元素2}  
在平时是int arr[],在go中就比较恶心,需要时间适应一下

初始化数组

#正常初始化,固定长度
var arr = [2]int{1,2}

#简短初始化,固定长度
arr := [2]int{1,2}

#自动计算长度
arr := [...]int{1,2,3,4,5}


len(arr) //计算数组长度

遍历数组(就是正常编程语言都有的for,后面条件控制再细讲for)

for i := 0; i < len(arr); i++{
    fmt.Println(arr[i])
}

for index, value := range arr {
    fmt.Println(index,value)
}

//不想使用下标的话可以用_匿名变量接受就可以不用管他了。
for _, value := range arr {
    fmt.Println(value)
}

数组中是不能使用append的,因为固定了长度。

切片

就是一个动态数组,可以对他进行空间上的操作,类似于c语言中new一个空间那样

所以切片解决了无法append添加的障碍,使用append需要用变量接受

var arr = []int{1, 2, 3, 4, 5, 6, 777, 999} //不加数组
fmt.Println(arr)
arr = append(arr,99999999)


//注意:make的时候长度要小于等于容量,容量为了让你添加元素的时候大于容量了用来扩容的
//在项目中给的容量要合适,否则大量扩容开销会很大。	
arr := make(数组类型, 长度, 容量)
arr := make(数组类型,长度) //容量默认和长度一样


cap(arr) //计算数组容量

image-20241111131622531

append

arr = append(list,1,2,3,4,5,6)  //切片想要增加多少元素自行添加,没有个数限制,但是记得添加完成后保存返回的数组

结构体

格式:

type 结构体名字 struct{
    变量1 类型
    变量2 类型
}

示例:

type Person struct{
    name string
    age int
    sex int
}

//结构体有点奇怪,和之前的普通类型赋值有点区别,人家int是这样的:var i int = 1,类型在变量名后面,等号前面
//而结构体这里就不是,变量名后面不用类型了,直接等于号,然后才是结构体类型。
var p = Person{name:"张三",age:18,sex:1}

//打印结构体中的变量的时候可以使用.来指定
fmt.Println(p.age)

image-20241113175753795

结构体方法

golang中的特色,因为golang没有类,所以就不能类.方法名,所以就有了结构体方法这种来解决

func (var *Struct_Name) FuncName( var0, var1... ) return type { }
(var *Struct_Name↑↑↑↑↑是接收器,每一个方法只能有一个接收器)


比如:
type Person struct{
    xxx xxx
}
func (p *Person) toString() string{
    return p.xxx
}
使用的时候就可以直接:
p := Person{
    xxx:xxx,    
}
fmt.Println(p.toString())

指针

和其他语言差不多,按照c语言的解释

*指针类型
&取地址
var p *int //这个就是定义指针变量,如果不给值的话默认=nil,这是golang中的空值
var num int = 123
p = &num
fmt.Println(p)   //直接拿到的就是地址值
fmt.Println(*p)  //*不在定义的时候使用就是解引用,将地址的值取出来

image-20241111135907451

打印地址与地址值

p是一个指针
var p *int = nil
var n2 int = 10
p = &n2
println(p) //出来是地址
fmt.Prinrtln(p) //出来也是地址

fmt.Println打印地址与地址值对结构体打印的时候就有差别

type test struct {
	s1 int
	s2 string
}

func main(){
    st := &test{  //st简短赋值,st是指针来的,因为赋值的是给他结构体指针
		s1: 1000,
		s2: "ok",
	}
	println(st)
	fmt.Println(st)
    
    //打印结构体内的变量,指针依旧是用.号来指定(这与c语言有点不同,c是需要结构体指针指向的时候用->符号)
    fmt.Println(st.s1)
}


//go真挺奇怪的,结构体这里又不给你打印地址,给你加个&,但是一些int,string等等都是会直接地址出来的

image-20241111104254301

map

格式:

map[键的类型]值的类型

定义的时候正常定义就行
var m = map[string]string
m := map[string]string

例子:(值的类型可以是很多)

var m = map[string]Person{
    "a":1,  //,逗号一定要有,不管有多少个,一定要添加上逗号,否则会报错
}
fmt.Println(m)
type Person struct{
    name string
    age int
}
m = map[string]Person
fmt.Println(m)

删除

m := map[string]int{
    "name":123
    "fff":222
}
delete(m, "name")  //删除键为name的元素

类型转换

在go中类型转换也是一样的操作

var a float64 = 3.14
var i int = int(a)  //强制转成int类型,那就是去掉小数点(不作四舍五入)

这里有个很好的例子

var s string = "hello"
var b []byte = []byte(s)
var s2 string = string(b)

如果要把int转string的话不可以直接string(int_type)
他会把你这个数字当成ascii来转成字符的

func main() {
    fmt.Println("打印ascii,通过string转换为字符:")

    fmt.Println(string(65))

    fmt.Println(string(66))

    fmt.Println(string(67))

}

导入包

这里和python导入包差不多,同样可以命名别名

import "fmt" //导入单个
import (  //导入多个
	"fmt"
    "math"
)

//给包起别名
import f "fmt"
//用的时候就可以不用fmt.Println了,可以f.Println使用你起的别名

字符串strings包

字符拼接

可以直接使用+号拼接

反引号: Hello, World! ,⽤于多⾏字符串和包含特殊字符的字符串

意思是你用反引号就可以原封不动输入你反引号内的东西,包括tab键回车换行等等。

特别是处理一些http请求回来的数据或者xml格式的数据非常好用

image-20241111142931497

Contains

匹配字符是否存在

strings.Contains(字符串1, "要匹配的字符串2")  //如果匹配的字符串2存在字符串1中的话返回true,否则false

Replace

fmt.Println(strings.Replace("hello world", "world", "Go", 1)) //hello go

ReplaceAll就是替换所有了

更多函数解释

函数 函数作⽤
Contains 检查字符串是否包含⼦字符串
ContainsAny 检查字符串是否包含任何⼀个给定字符
ContainsRune 检查字符串是否包含指定的 rune 字符
Count 计算⼦字符串在字符串中出现的次数
EqualFold 忽略⼤⼩写⽐较两个字符串是否相等
Fields 将字符串按空⽩字符分割成切⽚
HasPrefix 检查字符串是否以指定前缀开头
HasSuffix 检查字符串是否以指定后缀结尾
Index 返回⼦字符串在字符串中第⼀次出现的位置
IndexAny 返回字符串中任意⼀个字符第⼀次出现的位置
IndexByte 返回指定字节在字符串中第⼀次出现的位置
IndexRune 返回指定 rune 字符在字符串中第⼀次出现的位置
Join 将字符串切⽚⽤指定分隔符连接成⼀个字符串
LastIndex 返回⼦字符串在字符串中最后⼀次出现的位置
LastIndexAny 返回字符串中任意⼀个字符最后⼀次出现的位置
Repeat 重复字符串指定次数
Replace 替换字符串中的⼦字符串
ReplaceAll 替换字符串中的所有⼦字符串
Split 将字符串按指定分隔符分割成切⽚
SplitAfter 将字符串按指定分隔符分割成切⽚,保留分隔符
SplitAfterN 将字符串按指定分隔符分割成切⽚,保留分隔符,最多分割 N 次
SplitN 将字符串按指定分隔符分割成切⽚,最多分割 N 次
Title 将字符串的每个单词的⾸字⺟⼤写
ToLower 将字符串转换为⼩写
ToUpper 将字符串转换为⼤写
Trim 去除字符串两端的指定字符
TrimSpace 去除字符串两端的空⽩字符
TrimPrefix 去除字符串前缀
TrimSuffix 去除字符串后缀
NewReplacer 创建⼀个字符串替换器

输入输出

字符串格式化

常见的格式化占位符

%v:值的默认格式表示
%+v:类似%v,在结构体中会添加字段名
%T:值的类型
% %:百分号本身
%t:单词truefalse
%b:整数以⼆进制方式显示
%o:整数以八进制方式显示
%d:整数以十进制方式显示
%x:整数以十六进制方式显示
%c:相应Unicode码点表示的字符
%f:float类型输出,保留小数点几位可控,比如保留两位:%.2f
%e:科学计数法,如-1234.456e+78
%E:科学计数法,如-1234.456E+78
%s:解析变量,直接打印值,这个打印字符串常用,在你读取byte的时候,若byte为字符串也可以直接帮你打印字符串值
%q:打印值的时候加上双引号

fmt:Scanf、Scan、Scanln

Scanf

接收输入内容并输出,这个需要加格式化符号接收,类似C语言,与后续两个不一样,但都是需要给地址

var name string
fmt.Scanf("%s",&name) //接收用户输入的数据就要用取地址符号,跟c语言一样。
//这里的scanf双引号内不能有其他字符干扰,比如不能:"请输入字符:%s",这是接收不到用户输入的数据到name中的。
//PS:使用Scanf函数的时候他会返回两个值,第一个是是否接收到了数据:接收到了返回1,没有就返回0;第二个是返回错误信息(string)
//所以我们也可以这样子写接受的数据用户判断:_, err := fmt.Scanf("%s", &name)

//输出:name赋值成功后就可以正常输出了,按照对应的格式输出即可(使用fmt.Printf函数输出)
fmt.Printf("%s",name)

tips:

Scanf如果是单次获取多个值,使⽤空格隔开,并⾮回⻋,因为fmt.Scanf 的格式化字符串中,逗号(,)会被视为输⼊的⼀部分。
Scan

输入的时候空格或者回车隔开
(因为他会等待你输入完成符合接收的个数,如果你接受一个,那么你空格隔开后只接受第一个空格前面作为值)

在使用Scan的时候,用户输入完成之后,会返回两个值:用户输入的个数,错误信息

注意:使用他,如果你要输入两个值,就必须输入两个,如果要他输入三个值,就必须是三个。否则会一直阻塞。
其实说实话这个就有点扯了,毕竟你都要求输入几个了,还返回啥多少个个数呢是吧,一般人想的都是我不知道输入几个才会想着去打印知道我输入了几个。

var s string
var num int
count , err := fmt.Scan(&s,&num)
if err != nil{
    fmt.Println("错误信息:",err)
}else {
    fmt.Println("输入个数:",count)
}
Scanln

输入的时候用空格隔开,回车表示输入结束

var name, desc string
count, err := fmt.Scanln(&name, &desc)
if err != nil {
    fmt.Println("发生错误:", err)
}
fmt.Println("name:", name, "count:", count)

image-20241122214431009

fmt:Println、Print、Printf、Sprintf

Println
fmt.Println("1",2,name)  //输出并且在结尾加上回车换行,多个值可以用逗号隔开,不可以使用格式化字符串
Print

不接受可以格式化字符,对每一个操作数都相当于用了 %v(自动转合适的类型输出)。
不会回车换行

fmt.Println(str)
Printf

打印出格式化的字符串

fmt.Printf("%s",str)
fmt.Printf("%d",123)
Sprintf

格式化并返回一个字符串,不输出内容

fmt.Sprintf("%s",str)
fmt.Sprintf("%d",num)

条件控制

if else

if 条件 {
    ...
} else if{ //注意,else一定要在if的{后面接,如果回车换行写会报错。
    ...
} else {//注意,else一定要在if的{后面接,如果回车换行写会报错。
    ...
}

比如接着上面输入输出的代码进一步判断

var (
    name string
)
_, err := fmt.Scanf("%s", &name)
if err != nil {
    fmt.Println("没有接受到输入数据。")
} else {
    fmt.Printf("name = %s", name)
}

练习:要求用户输入姓名、性别、年龄

package main

import (
	"fmt"
)

func main() {
	var (
		name  string
		age   int
		score float64
	)

	fmt.Println("请输入姓名、年龄、分数:(例如:张三 25 89.5):")
	_, err := fmt.Scanf("%s %d %f", &name, &age, &score)
	if err != nil {
		fmt.Println("输出错误")
	}

	fmt.Printf("姓名:%s\n年龄:%d\n分数:%.2f", name, age, score)
}

Switch

格式示例

color := "blue"
switch day {
    case "red", "深红":  //可以多个条件算一个case
    	fmt.Println("红色")
    case "blue", "深蓝":
    	fmt.Println("蓝色")
    case "black":
    	fmt.Println("黑色")
    default:
    	fmt.Println("不知道")

练习:区分工作日和周末

var (
    day string
)
fmt.Println("请输入星期几:(星期一 or 1)")
fmt.Scanf("%s", &day)
switch day {
	case "星期一", "1", "星期二", "2", "星期三", "3", "星期四", "4", "星期五", "5":
	    fmt.Println("工作日")
	case "星期六", "6", "星期日", "7":
	    fmt.Println("周末")
	default:
	    fmt.Println("给我干哪了,这还是地球么?")
}

For

条件循环

//打印0-9
for i := 0; i < 10; i++{
    fmt.Println(i)
}

死循环

//死循环
for ;; {
    fmt.Println("666")
}

range提取循环

var arr = []int{1,2,3,4,5,6,7,8888}
for index, value := range arr{
    fmt.Printf("index: %d, value:%d\n", index,value)
}

//不想使用下标的话可以用_匿名变量接受就可以不用管他了。

tips:

range使用的时候,切记要记得 :=赋值, for _,value := range arr

函数

格式

在上面学习中其实已经了解了函数是长啥样了,这里正式说一下, 同时需要注意一些细节

func 函数名([参数名]) {
    ...
}

比如最常见的:
func main(){
    fmt.Println("123")
}

可数的参数变量函数

func f(name string, age int){
    ...
}

可变参数相同类型参数变量函数

func f(arr ...int){
    for _,i range arr{
        fmt.Println(i)  //接收多个参数为一个数组,然后遍历即可。
    } 
}

返回一个值

格式:
func f()返回的值类型{
    ...
}
func f()(返回的值类型){
    ...
}

例如:
func f()int{
    return 1111
}

返回多个值

格式:
func f()(返回的值1类型,返回的值2类型...){
    
}

例如:
func f()(int,string){
    return 1, "123"
}

匿名函数

格式:
func(a int, s string){
    fmt.Println(a,s)
}(123,"123")

初始化函数

初始化函数会在main函数运行之前运行,比如你项目需要在运行之前做一些前置初始化动作可以放在init函数执行。

func init() {
	fmt.Println("我是初始化init函数。")
}

指定变量名作为返回值

/**
需要注意的点:
在返回值的地方先定义了一个变量,然后在函数内就不用定义该变量了,直接赋值即可。
不可以和传入的参数名相同,因为也是在做定义变量的操作。
**/
func fff(a int, b int) (x int, y int, c int) {
	x = a
	y = b
	c = x + y
	return
}
func main(){
    x, y, c := fff(4, 4)
	fmt.Println(x, y, c)
}
//输出:4 4 8

重载方法Methods

方法接受者。

由于go中没有类这种说法,所以只能是在结构体中作文章,那么go中就提供了这种叫做方法接受者,就是类似于 类.方法 ,方法前面跟一个结构体(可以是指针,指针是不用拷贝)

用法:

func (变量名 结构体)方法名 (){}
func (变量名 *结构体)方法名 (){}
在方法名前面加上↑↑↑,就表示这个结构体实现了这个方法,后续使用的时候就可以结构体.方法使用这个方法了

方法的其他返回值或者接受的参数的方式都没变,只是在方法名前作为结构体实现的方法而已。

简单例子:

package main

import "fmt"

type Person struct {
	name string
	age  int
}

func (p Person) getName() string {
	return p.name
}

func main() {
	p := Person{
		name: "zhangsan",
		age:  12,
	}
	fmt.Println(p.getName())

}

⽅法表达式

方法表达式使用 结构体.方法(*结构体).方法

两种方式其实都可以,使用指针的就是需要对你结构体中某个变量进行修改就要用到指针去接收方法表达式

使用表达式:

  1. 方法名(结构体变量,参数1,参数2,,,,)
  2. 方法名(&结构体变量,参数1,参数2,,,,)
type Circle struct {
	radius float64
}

func (c Circle) Area() float64 {
	//计算圆的面积:π * r的平方
	return (math.Pi * c.radius * c.radius)
}
func (c *Circle) Scale(f float64) {
	c.radius = c.radius * f //缩放
}
func main() {
	c := Circle{
		radius: 2,
	}
	f := Circle.Area
	fmt.Printf("圆的面积是:%.2f\n", f(c))
	f2 := (*Circle).Scale   
	f2(&c, 2) //半径扩大两倍
	fmt.Printf("缩放2倍后的面积为:%.2f\n", f(c))
}

defer(很重要,对理解通道和遍历通道)

现总结:
通道后面会解释,这里先暂时知道通道这个词即可…
如果你要对通道进行遍历,一定要在遍历之前记性关闭通道,通常在协程中defer进行关闭,意思是当前进行结束就会关闭通道,那么这就正好符合了协程ok后才彻底关闭通道,否则你主函数和go协程这两个不是同一个东西,协程操作了通道,你又要在主函数中进行关闭的话就会报错。

遍历通道解决办法就是:

使用了go协程:可以利用defer直接在go协程中关闭,或者使用sync.WaitGroup,但是你要是用sync.WaitGroup关闭的话也要在主进程中

go func() {
	wg.Wait() // 等待 
	close(ch) // 关闭通道 
}()

上面这段在后面也会提到。

然后如果直接在主进程中进行遍历的话直接close通道就行,因为没有协程需要等待。

理解:
defer你只需要理解为帮助函数后执行。
比如:当你打开文件,打开文件后需要关闭,但是中间操作的时候报错了就产生了资源浪费,这时候就可以给defer f.close()进行关闭,可以理解为main函数中就算中途报错结束了,defer也会帮你关闭f句柄。


多个 defer 的话会反过来执行,比如 
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
输出
3
2
1

下面经典举例子:
下面的函数和其他技术在后面都会提到,这里只是作为一个例子先放上来作为理解的知识点,主要还是方便复习,初学可以忽略,学完基础再回来理解也是挺不错的。

func start_Ch_scan_port() {
	var (
		ch        = make(chan int)
		count int = 0
	)
	var Ch_scan_port = func(hostname string) {
		defer close(ch)   //如果你不在这里添加关闭通道,那么你在主函数中关闭通道的话,你是无法对通道关闭然后遍历的。
        				//因为你在主进程关闭通道的话就会发生报错,因为协程经常会还没执行完成就被抢占过去然后close就会导致一些还没执行完成的进程执行失败。
		for i := 0; i < 65535; i++ {
			//扫描到开放端口放到ch通道中即可,主函数一直等待读取通道信息
			address := fmt.Sprintf("%s:%d", hostname, i)                //ip端口
			conn, err := net.DialTimeout("tcp", address, 2*time.Second) //2秒
			if err == nil {
				conn.Close()
				ch <- i
			}

		}
		runtime.Goexit()
	}

	go Ch_scan_port("127.0.0.1")
	runtime.Gosched() //等待go协程执行完
    //正常思维都是在这里go协程执行完成后就在这里进行close,因为你后面要对通道进行遍历了,但是在这里进行close(ch)的话就会发生报错,因为Gosched之后,在go协程中最后那几个可能还没彻底执行完,所以就会报错。
	for i := range ch {
		fmt.Printf("open: %d\n", i)
		count++
	}
	fmt.Printf("------------Open ports: %d-------------\n", count)
	fmt.Println("------------Scan——Done------------")
}

基础语法阶段练习

简单编码解码器

要求:

/*
根据⽤户输⼊的内容,实现切换编解码的功能.
支持base64 url hex编解码
功能1:选择编码还是解码
--->功能1.2:选择编解码的类型
------->功能1.2.3:输入内容进行编解码
功能2:exit才是推出

//编码
to := dongle.Encode.FromString(x).ByBase64().ToString()
//解码
to1 := dongle.Decode.FromString(q).ByBase64().ToString()
*/

可以参考的屎山代码如下:

package main

import (
	"fmt"

	"github.com/dromara/dongle"
)

/*
根据⽤户输⼊的内容,实现切换编解码的功能.
支持base64 url hex编解码
功能1:选择编码还是解码
--->功能1.2:选择编解码的类型
------->功能1.2.3:输入内容进行编解码
功能2:exit才是推出

//编码
to := dongle.Encode.FromString(x).ByBase64().ToString()
//解码
to1 := dongle.Decode.FromString(q).ByBase64().ToString()
*/

func choice_de_en(choice string, flag *bool) bool {
	var result string
	switch choice {
	case "1": //选择编码模式
		result = encode(flag)
		if *flag == false {
			return false //退出程序
		}
		fmt.Printf("编码结果:%s\n", result)
	case "2": //选择解码模式
		result = decode(flag)
		if *flag == false {
			return false //退出程序
		}
		fmt.Printf("解码结果:%s\n", result)
	}
	return true
}

func choice_string_type(mess string) string {
	fmt.Printf("请输入数字选择%s码类型 1:Base64 2:Url 3:Hex (输入exit为退出程序)\n", mess)
	var crypto_type string
	fmt.Scanln(&crypto_type)
	return crypto_type
}

func decode(flag *bool) string {
	crypto_type := choice_string_type("解")
	if crypto_type == "exit" {
		*flag = false
		return ""
	}
	fmt.Println("请输入你要解码的密文:")
	var txt string
	fmt.Scanln(&txt)
	switch crypto_type {
	case "1": //base64解码
		return dongle.Decode.FromString(txt).ByBase64().ToString()
	case "2": //url解码
		return dongle.Decode.FromString(txt).BySafeURL().ToString()
	case "3": //hex解码
		return dongle.Decode.FromString(txt).ByHex().ToString()
	default:
		return ""
	}
}

func encode(flag *bool) string {
	crypto_type := choice_string_type("编")
	if crypto_type == "exit" {
		*flag = false
		return ""
	}
	fmt.Printf("请输入你要编码的明文:")
	var txt string
	fmt.Scanln(&txt)
	switch crypto_type {
	case "1": //base64编码
		return dongle.Encode.FromString(txt).ByBase64().ToString()
	case "2": //url编码
		return dongle.Encode.FromString(txt).BySafeURL().ToString()
	case "3": //hex编码
		return dongle.Encode.FromString(txt).ByHex().ToString()
	default:
		return ""
	}
}

func main() {

	for {
		var choice string
		fmt.Println("请输入数字选择模式 1:编码模式 2:解码模式 (输入exit为退出程序)")
		fmt.Scanln(&choice)
		if choice == "exit" {
			break
		}
		flag := true  //作为退出程序的标记,方便不管在哪个函数内都能识别到用户是否选择退出了
		if !choice_de_en(choice, &flag) { //如果返回false就退出程序
			break
		}
	}
}

image-20241114003710217

简单图书馆

功能需求

添加新书到图书馆。 查找书籍信息。
列出所有书籍。 更新书籍信息。 删除书籍。

可以参考的屎山代码如下:

package main

import (
	"fmt"
	"strconv"
)

type Book struct {
	name   string
	author string
	page   int
}

func (b *Book) toString() string { //打印结构体,这里int转sting用到了strconv包
	return " | " + "书名:" + string(b.name) + " | " + "作者:" + string(b.author) + " | " + "页数:" + strconv.Itoa(b.page) + " | "
}
func addBook(lib *map[string]Book, new_book Book) {
	(*lib)[new_book.name] = new_book
	fmt.Printf("成功添加书籍 \"%s\" 到图书馆中。\n", new_book.name)
}
func findBook(lib *map[string]Book, name string) Book {
	return (*lib)[name]
}
func listBook(lib *map[string]Book) {
	fmt.Println("=============图书馆中的书籍=============")
	for _, v := range *lib {
		fmt.Println(v.toString()) //直接遍历打印即可
	}
	fmt.Println("=======================================")
}
func updateBook(lib *map[string]Book, new_book Book) { //因为map中可以直接改,所以和添加book方式一样
	(*lib)[new_book.name] = new_book
	fmt.Printf("\"%s\"书籍更新成功\n", new_book.name)
}
func deleteBook(lib *map[string]Book, name string) {
	delete((*lib), name) //删除键为name的元素
	fmt.Printf("\"%s\" 书籍已删除\n", name)
}
func main() {
	/*
	   添加新书到图书馆。(添加go/python书籍)
	   查找书籍信息。(查找go书籍)
	   列出所有书籍。
	   更新书籍信息。(更新go书籍的页数)
	   删除书籍。(删除python书籍)
	*/
	lib := make(map[string]Book)
	//添加新书到图书馆。(添加go/python书籍)
	gobook := Book{
		name:   "Go 编程",
		author: "李四",
		page:   123,
	}
	addBook(&lib, gobook)
	pythonbook := Book{
		name:   "Python 编程",
		author: "张三",
		page:   456,
	}
	addBook(&lib, pythonbook)

	//查找书籍信息。(查找go书籍)
	findBook(&lib, "Go 编程")
	findBook(&lib, "Go 编程")

	//列出所有书籍。
	listBook(&lib)

	//更新书籍信息。(更新go书籍的页数)
	gobook.page = 9999999999999
	updateBook(&lib, gobook)
	//列出所有书籍。
	listBook(&lib)

	//删除书籍。(删除python书籍)
	deleteBook(&lib, pythonbook.name)
	//列出所有书籍。
	listBook(&lib)
}

http://www.niftyadmin.cn/n/5869231.html

相关文章

【Python爬虫(50)】从0到1:打造分布式爬虫项目全攻略

【Python爬虫】专栏简介&#xff1a;本专栏是 Python 爬虫领域的集大成之作&#xff0c;共 100 章节。从 Python 基础语法、爬虫入门知识讲起&#xff0c;深入探讨反爬虫、多线程、分布式等进阶技术。以大量实例为支撑&#xff0c;覆盖网页、图片、音频等各类数据爬取&#xff…

【Spring详解六】容器的功能扩展-ApplicationContext

六、容器的功能扩展_ApplicationContext 经过前几章的分析&#xff0c;对Spring中的容器功能有了简单的了解&#xff0c;在前面几章中一直以 BeanFactory接口以及它的默认实现类XmlBeanFacotory为例进行分析&#xff0c;但是Spring中还提供了 另一个接口ApplicationContext&…

prometheus+node_exporter+grafana监控K8S信息

prometheusnode_exportergrafana监控K8S 1.prometheus部署2.node_exporter部署3.修改prometheus配置文件4.grafana部署 1.prometheus部署 包下载地址&#xff1a;https://prometheus.io/download/ 将包传至/opt 解压 tar xf prometheus-2.53.3.linux-amd64.tar.gz 移动到…

华为hcia——Datacom实验指南——二层交换原理

实验配置 eNSP 什么是二层交换 二层交换是指在同一个ip网段内&#xff0c;数据通过二层交换机进行转发。 什么是mac地址 mac地址也叫做硬件地址&#xff0c;是以太网协议的链路层地址。简单的来说&#xff0c;mac地址就是我们硬件的身份证&#xff0c;独一无二。它是由48个bi…

电子商务网站租用香港服务器的好处有哪些?

电子商务网站租用香港服务器的好处主要包括&#xff1a; 香港服务器提供高速的网络连接&#xff0c;国内访问速度优势明显&#xff0c;满足企业内部数据传输和远程办公需求。拥有国际出口带宽优势&#xff0c;实现与全球各地的高速连接&#xff0c;对跨国业务和海外市场拓展至关…

全价值链数字化转型:以美的集团为例,探索开源AI大模型与S2B2C商城小程序源码的融合应用

摘要&#xff1a;在数字经济时代背景下&#xff0c;企业面临着前所未有的竞争压力与市场变革。全价值链的数字化转型&#xff0c;作为提升企业核心竞争力的关键策略&#xff0c;正逐步成为行业共识。美的集团&#xff0c;作为家电行业的领军企业&#xff0c;其基于数字化的全价…

Java 大视界 —— Java 大数据在智慧能源微电网能量管理中的关键技术(100)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

政安晨【零基础玩转各类开源AI项目】DeepSeek 多模态大模型Janus-Pro-7B,本地部署!支持图像识别和图像生成

政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; 目录 下载项目 创建虚拟环境 安装项目依赖 安装 Gradio&#xff08;UI&#xff09; 运…