Go语言入门进阶语法 | 数据结构 |指针|结构体|数组|切片|Map|方法|接口|错误|io库|泛型

✅作者简介:CSDN内容合伙人、信息安全专业在校大学生🏆
🔥系列专栏 :
📃新人博主 :欢迎点赞收藏关注,会回访!
💬舞台再大,你不上台,永远是个观众。平台再好,你不参与,永远是局外人。能力再大,你不行动,只能看别人成功!没有人会关心你付出过多少努力,撑得累不累,摔得痛不痛,他们只会看你最后站在什么位置,然后羡慕或鄙夷。


文章目录

    • 进阶数据类型
      • 指针
      • 结构体
      • 数组
      • 切片
      • `map`
    • 方法
      • 声明和调用
      • 方法与指针重定向
      • 选择值或者指针来作为接受者
    • 接口
      • 声明
      • 实现接口
      • 使用
      • 类型断言和类型切换
        • 类型断言
        • 类型切换
      • `Stringer`
    • 错误
    • io流和os库
      • Reader
        • `os.ReadFile()` 一次读取整个文件内容并返回,不适宜太大的文件
        • `os.ReadDir()`读取目录下的所有文件名并返回
      • Writer
      • os库
        • `os.Create()`
        • `os.Open()`
      • 带有缓冲区的读写:bufio
    • 图像
    • 泛型
      • 泛型函数
      • 泛型类型
    • 并发
      • 协程
      • 信道
      • range和close
      • select

进阶数据类型

指针

var num int = 10 num是值 &num是地址
var p *int = &num p是指针,指向num的底层地址,p是num的值,修改p会修改底层数据

package mainimport "fmt"func main() {var num int = 10var p *int = &numfmt.Printf("num = %d, p = %d, *p = %d\n", num, p, *p)//num = 10, p = 824633761976, *p = 10*p++fmt.Printf("num = %d, p = %d, *p = %d\n", num, p, *p)//num = 11, p = 824633761976, *p = 11}
package mainimport "fmt"func main() {i, j := 42, 2701p := &i         // 指向 ifmt.Println(*p) // 通过指针读取 i 的值   42*p = 21         // 通过指针设置 i 的值   21fmt.Println(i)  // 查看 i 的值p = &j         // 指向 j*p = *p / 37   // 通过指针对 j 进行除法运算fmt.Println(j) // 查看 j 的值            73
}

结构体

// 定义一个结构体
type Vertex struct {X intY int
}func main() {// 声明一个结构体变量v := Vertex{1, 2}   // 亦可显示赋值 Vertex{X:1, Y:2} // 字段可以通过 . 来访问v.X = 4  // 指向结构体的指针p := &vp.X = 1e9  // 按理来说应该是 *p.X 但是可以简写}

数组

数组的长度是其类型的一部分,因此数组不能改变大小。 这看起来是个限制,不过没关系,Go 拥有更加方便的使用数组的方式。

func main() {var a [2]stringa[0] = "Hello"a[1] = "World"fmt.Println(a[0], a[1]) // Hello Worldfmt.Println(a) // [Hello World]primes := [6]int{2, 3, 5, 7, 11, 13}fmt.Println(primes) //[2 3 5 7 11 13]
}

切片

  1. 每个数组的大小都是固定的。而切片则为数组元素提供了动态大小的、灵活的视角。 在实践中,切片比数组更常用。
func main() {primes := [6]int{2, 3, 5, 7, 11, 13}var s []int = primes[1:4] //[3 5 7]fmt.Println(s)
}
  1. 切片类似数组的引用,改变切片的值会影响原来数组的值
func main() {names := [4]string{"John","Paul","George","Ringo",}fmt.Println(names) //[John Paul George Ringo]a := names[0:2]b := names[1:3]fmt.Println(a, b) //[John Paul] [Paul George]b[0] = "XXX"fmt.Println(a, b) //[John XXX] [XXX George]fmt.Println(names) //[John XXX George Ringo]
}
  1. 切片字面量 简单来说就是允许你 直接声明并初始化一个切片,相当于底层创建了数组,然后引用数组
  2. 长度和容量 len(slice) cap(slice)
  3. make 创建切片,不返回指针 , new返回指针
1. 切片可以用内置函数 make 来创建,这也是你创建动态数组的方式。make 函数会分配一个元素为零值的数组并返回一个引用了它的切片:a := make([]int, 5)  // len(a)=5要指定它的容量,需向 make 传入第三个参数:b := make([]int, 0, 5) // len(b)=0, cap(b)=5b = b[:cap(b)] // len(b)=5, cap(b)=5
b = b[1:]      // len(b)=4, cap(b)=4
  1. 追加元素
func main() {var s []intprintSlice(s) //len=0 cap=0 []// 可在空切片上追加s = append(s, 0)printSlice(s) //len=1 cap=1 [0]// 这个切片会按需增长s = append(s, 1)printSlice(s) //len=2 cap=2 [0 1]// 可以一次性添加多个元素s = append(s, 2, 3, 4)printSlice(s) //len=5 cap=6 [0 1 2 3 4]
}func printSlice(s []int) {fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}

map

  1. 创建语法
1. 
map1 = make(map[keyType]valueType)
map1[key1] = value1
2. 
map[keyType]valueType{key1:value1,key2:value2
}
  1. 修改 获取 删除
func main() {m := make(map[string]int)m["答案"] = 42fmt.Println("值:", m["答案"]) // 值: 42m["答案"] = 48fmt.Println("值:", m["答案"]) // 值: 48delete(m, "答案")fmt.Println("值:", m["答案"]) // 值: 0v, ok := m["答案"]fmt.Println("值:", v, "是否存在?", ok) //值: 0 是否存在? false
}

方法

声明和调用

go没有类,方法就是一类带特殊的 接收者 参数的函数。 就是函数的参数是特定的结构体
就是接收者的类型定义和方法声明必须在同一包内

type Vertex struct {X, Y float64
}
// 1. 写法1  函数
func Abs(v Vertex) float64 {return math.Sqrt(v.X*v.X + v.Y*v.Y)
}	
// 2. 写法2  方法
func (v Vertex) Abs() float64 {return math.Sqrt(v.X*v.X + v.Y*v.Y)
}func main() {v := Vertex{3, 4}fmt.Println(Abs(v)) //5
}
  • 指针类型的接受者
type Vertex struct {X, Y float64
}func (v Vertex) Abs() float64 {return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
//  若去掉下面的 *  则变成了值传递 而不是引用传递
func (v *Vertex) Scale(f float64) {v.X = v.X * fv.Y = v.Y * f
}func main() {v := Vertex{3, 4}   // 等价于 p = &Vertex{3, 4}  这样p就是真正的指针了v.Scale(10)fmt.Println(v.Abs()) // 50
}
  • 下面的和上面实现功能一致,只是用函数来表示: (方法重写为函数)
type Vertex struct {X, Y float64
}func Abs(v Vertex) float64 {return math.Sqrt(v.X*v.X + v.Y*v.Y)
}func Scale(v *Vertex, f float64) {v.X = v.X * fv.Y = v.Y * f
}func main() {v := Vertex{3, 4}Scale(&v, 10)fmt.Println(Abs(v))
}

方法与指针重定向

接受一个值作为参数的函数必须接受一个指定类型的值:

var v Vertex
fmt.Println(AbsFunc(v))  // OK
fmt.Println(AbsFunc(&v)) // 编译错误!

而以值为接收者的方法被调用时,接收者既能为值又能为指针:

var v Vertex
fmt.Println(v.Abs()) // OK
p := &v
fmt.Println(p.Abs()) // OK

这种情况下,方法调用 p.Abs() 会被解释为 (*p).Abs()

选择值或者指针来作为接受者

使用指针接收者的原因有二:
首先,方法能够修改其接收者指向的值。
其次,这样可以避免在每次调用方法时复制该值。若值的类型为大型结构体时,这样会更加高效。
在本例中,ScaleAbs 接收者的类型为 *Vertex,即便 Abs 并不需要修改其接收者。
通常来说,所有给定类型的方法都应该有值或指针接收者,但并不应该二者混用。 (我们会在接下来几页中明白为什么。)

type Vertex struct {X, Y float64
}func (v *Vertex) Scale(f float64) {v.X = v.X * fv.Y = v.Y * f
}func (v *Vertex) Abs() float64 {return math.Sqrt(v.X*v.X + v.Y*v.Y)
}func main() {v := &Vertex{3, 4}fmt.Printf("缩放前:%+v,绝对值:%v\n", v, v.Abs())//缩放前:&{X:3 Y:4},绝对值:5v.Scale(5)fmt.Printf("缩放后:%+v,绝对值:%v\n", v, v.Abs())//缩放后:&{X:15 Y:20},绝对值:25
}

接口

接口类型 的定义为一组方法签名。
接口类型的变量可以持有任何实现了这些方法的值。

声明

type Shape interface {  Area() float64  Perimeter() float64  
}

实现接口

在Go语言中,如果一个类型拥有接口中所有的方法,那么这个类型就实现了该接口,而不需要显式声明“我实现了这个接口”。这种隐式接口的方式是Go语言的一个独特之处。

type Circle struct {  radius float64  
}  // Circle类型实现了Shape接口  
func (c Circle) Area() float64 {  return math.Pi * c.radius * c.radius  
}  func (c Circle) Perimeter() float64 {  return 2 * math.Pi * c.radius  
} 

使用

  1. 作为函数参数
func PrintShapeInfo(s Shape) {  fmt.Printf("Area: %v, Perimeter: %v\n", s.Area(), s.Perimeter())  
}  func main() {  c := Circle{radius: 5}  PrintShapeInfo(c)  
}
  1. 作为类型断言和类型切换
var s Shape = Circle{radius: 5}  if c, ok := s.(Circle); ok {  fmt.Println("Circle radius:", c.radius)  
}  // 或者使用类型切换  
switch v := s.(type) {  
case Circle:  fmt.Println("Circle radius:", v.radius)  
case Rectangle:  fmt.Println("Rectangle dimensions:", v.width, v.height)  
default:  fmt.Println("Unknown shape")  
}

类型断言和类型切换

类型断言

类型断言 提供了访问接口值底层具体值的方式。
t := i.(T)
该语句断言接口值 i 保存了具体类型 T,并将其底层类型为 T 的值赋予变量 t
i 并未保存 T 类型的值,该语句就会触发一个 panic。
为了 判断 一个接口值是否保存了一个特定的类型,类型断言可返回两个值:其底层值以及一个报告断言是否成功的布尔值。
t, ok := i.(T)
i 保存了一个 T,那么 t 将会是其底层值,而 oktrue
否则,ok 将为 falset 将为 T 类型的零值,程序并不会产生 panic。
请注意这种语法和读取一个映射时的相同之处。

func main() {var i interface{} = "hello"s := i.(string)fmt.Println(s) //hellos, ok := i.(string)fmt.Println(s, ok)//hello truef, ok := i.(float64)fmt.Println(f, ok)//0 falsef = i.(float64) //panic: interface conversion: interface {} is string, not float64fmt.Println(f)
}
类型切换
func do(i interface{}) {switch v := i.(type) {case int:fmt.Printf("二倍的 %v 是 %v\n", v, v*2)case string:fmt.Printf("%q 长度为 %v 字节\n", v, len(v))default:fmt.Printf("我不知道类型 %T!\n", v)}
}func main() {do(21)do("hello")do(true)
}

Stringer

fmt 包中定义的 Stringer 是最普遍的接口之一。

type Stringer interface {String() string
}

Stringer 是一个可以用字符串描述自己的类型。fmt 包(还有很多包)都通过此接口来打印值。 类似于Java的toString

type Person struct {Name stringAge  int
}
// 重写 String()方法
func (p Person) String() string {return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}func main() {a := Person{"Arthur Dent", 42}z := Person{"Zaphod Beeblebrox", 9001}fmt.Println(a, z) //Arthur Dent (42 years) Zaphod Beeblebrox (9001 years)
}

错误

Go 程序使用 error 值来表示错误状态。
fmt.Stringer 类似,error 类型是一个内建接口:

type error interface {Error() string
}

(与 fmt.Stringer 类似,fmt 包也会根据对 error 的实现来打印值。)
通常函数会返回一个 error 值,调用它的代码应当判断这个错误是否等于 nil 来进行错误处理。

i, err := strconv.Atoi("42")
if err != nil {fmt.Printf("couldn't convert number: %v\n", err)return
}
fmt.Println("Converted integer:", i)

error 为 nil 时表示成功;非 nil 的 error 表示失败。

io流和os库

io - Go 编程语言
io

Reader

io 包指定了 io.Reader 接口,它表示数据流的读取端。
Go 标准库包含了该接口的许多实现,包括文件、网络连接、压缩和加密等等。
io.Reader 接口有一个 Read 方法:

func (T) Read(b []byte) (n int, err error)

Read 用数据填充给定的字节切片并返回填充的字节数和错误值。在遇到数据流的结尾时,它会返回一个 io.EOF 错误。
示例代码创建了一个 strings.Reader 并以每次 8 字节的速度读取它的输出。

package mainimport ("fmt""io""strings"
)func main() {r := strings.NewReader("Hello, Reader!")b := make([]byte, 8)for {n, err := r.Read(b)fmt.Printf("n = %v err = %v b = %v\n", n, err, b)fmt.Printf("b[:n] = %q\n", b[:n])if err == io.EOF {break}}
}
/*
n = 8 err = <nil> b = [72 101 108 108 111 44 32 82]
b[:n] = "Hello, R"
n = 6 err = <nil> b = [101 97 100 101 114 33 32 82]
b[:n] = "eader!"
n = 0 err = EOF b = [101 97 100 101 114 33 32 82]
b[:n] = ""
*/
os.ReadFile() 一次读取整个文件内容并返回,不适宜太大的文件
import ("fmt""os"
)const FILENAME = "test.txt"func TestReadFile() {file, err := os.ReadFile(FILENAME)if err != nil {return}fmt.Printf("读取文件内容: %s\n", file)
}
os.ReadDir()读取目录下的所有文件名并返回
func TestReadeDir() {dir, err := os.ReadDir(DIRPATH)if err != nil {return}fmt.Printf("读取目录内容: %s\n", dir)
}

Writer

type Writer interface {  Write(p []byte) (n int, err error)  
}
一般都是使用它已经被继承的接口1. 直接写入
os.WriteFile("example.txt", []byte("Hello, Go!\n"), 0644)  2. 打开文件获取文件指针之后写入  
file, err := os.Open(FILENAME)
file.Write(b []byte)
file.WriteAt(b []byte, off int64)
file.WriteString(s string)3. bufio.Writer 进行缓冲写入
writer := bufio.NewWriter(file)  
_, err = writer.WriteString("Hello, Go!\n")  
if err != nil {  fmt.Println("Error writing to buffer:", err)  return  
}  err = writer.Flush() // 确保所有数据都被写入到底层io.Writer中  
if err != nil {  fmt.Println("Error flushing buffer:", err)  return  
}

os库

image.png

os.Create()
func TestCreateFile() {// 创建或覆盖文件file, err := os.Create("test_create.txt")if err != nil {fmt.Printf("创建文件失败: %s\n", err)return}defer file.Close()// 写入_, err = file.WriteString("Hello, Go!\n")if err != nil {fmt.Printf("写入文件失败: %s\n", err)return}fmt.Printf("写入文件成功\n")}
os.Open()

对于更复杂的读取操作,如逐行读取,可以使用bufio.Scanner,它封装了io.Reader接口,提供了方便的逐行读取功能。

func TestOpen() {file, err := os.Open(FILENAME)if err != nil {fmt.Printf("打开文件失败: %s\n", err)return}defer file.Close()// 使用bufio.NewScanner读取文件scanner := bufio.NewScanner(file)for scanner.Scan() {fmt.Println(scanner.Text()) // 输出每一行}if err := scanner.Err(); err != nil {fmt.Println("Error reading file:", err)return}// 或者使用  dstByte = make([]byte, num) // 使用num来控制一次读几个字节n, err := file.Read(dstByte) // 读内容到缓冲区fmt.Println("读取文件成功")
}

带有缓冲区的读写:bufio

  1. bufio.Reader

bufio.Reader是bufio包中用于读取数据的结构体,它实现了io.Reader接口,并提供了缓冲功能。常用的方法有:

ReadString(delim byte) (string, error):读取直到遇到指定的分隔符delim为止的字符串。
ReadBytes(delim byte) ([]byte, error):功能与ReadString类似,但返回的是[]byte类型的数据。
ReadByte() (byte, error):读取单个字节。
ReadRune() (rune, int, error):读取一个Unicode码点(rune),返回码点、其编码长度和可能的错误。
Peek(n int) ([]byte, error):预览接下来的n个字节,但不移动读取位置。
UnreadRune() error:吐出最近一次读取操作读取的最后一个rune(只能吐出最后一个,多次调用会出错)。
Read(p []byte) (n int, err error):从输入中读取数据到p中,返回读取的字节数和可能遇到的错误。
  1. bufio.Writer

bufio.Writer是bufio包中用于写入数据的结构体,它实现了io.Writer接口,并提供了缓冲功能。常用的方法有:

Write(p []byte) (n int, err error):将p中的数据写入缓冲区。
WriteString(s string) (n int, err error):将字符串s写入缓冲区。
WriteByte(c byte) error:将单个字节c写入缓冲区。
Flush() error:将缓冲区中的数据写入到底层的io.Writer中,并清空缓冲区。
  1. bufio.Scanner

bufio.Scanner是一个用于扫描输入数据的结构体,它提供了按行、按分隔符或按其他条件扫描输入的功能。常用的方法有:

Scan() bool:扫描输入直到遇到分隔符或输入结束,返回是否成功扫描到数据。
Text() string:返回最后一次扫描到的文本数据。
Bytes() []byte:功能与Text()类似,但返回的是[]byte类型的数据。
Split(split func(data []byte, atEOF bool) (advance int, token []byte, err error)):设置扫描器的分割函数,用于定义如何分割输入数据。

图像

image 包定义了 Image 接口:

package imagetype Image interface {ColorModel() color.ModelBounds() RectangleAt(x, y int) color.Color
}

注意:Bounds 方法的返回值 Rectangle 实际上是一个 image.Rectangle,它在 image 包中声明。
(请参阅文档了解全部信息。)
color.Colorcolor.Model 类型也是接口,但是通常因为直接使用预定义的实现 image.RGBAimage.RGBAModel 而被忽视了。这些接口和类型由 image/color 包定义。

泛型

**泛型(Generics)**是一种编程范式,允许在编写代码时暂时不指定具体的类型,而是在使用时才确定。这样可以使代码更加通用,减少重复,并增强类型安全。Go语言从1.18版本开始正式支持泛型。
泛型语法:

  • 使用方括号[]来定义类型参数列表,如[T any]
  • 类型参数后面可以跟一个类型约束,用于限制类型参数可以取哪些类型。any表示任意类型,也可以使用接口来定义更具体的约束。

泛型函数

可以使用类型参数编写 Go 函数来处理多种类型。 函数的类型参数出现在函数参数之前的方括号之间。
func Index[T comparable](s []T, x T) int
此声明意味着 s 是满足内置约束 comparable 的任何类型 T 的切片。 x 也是相同类型的值。
comparable 是一个有用的约束,它能让我们对任意满足该类型的值使用 ==!= 运算符。在此示例中,我们使用它将值与所有切片元素进行比较,直到找到匹配项。 该 Index 函数适用于任何支持比较的类型。

// 假如是整形 是不是这样表示?
func IndexInt (s []int, x int) int {pass
}
// Index 返回 x 在 s 中的下标,未找到则返回 -1。
func Index[T comparable](s []T, x T) int {for i, v := range s {// v 和 x 的类型为 T,它拥有 comparable 可比较的约束,// 因此我们可以使用 ==。if v == x {return i}}return -1
}func main() {// Index 可以在整数切片上使用si := []int{10, 20, 15, -10}fmt.Println(Index(si, 15)) //2// Index 也可以在字符串切片上使用ss := []string{"foo", "bar", "baz"}fmt.Println(Index(ss, "hello")) //-1
}

泛型类型

除了泛型函数之外,Go 还支持泛型类型。 类型可以使用类型参数进行参数化,这对于实现通用数据结构非常有用。

type Stack[T any] []T

这里,Stack是一个泛型类型,它接受一个类型参数TT可以是任意类型(因为使用了any作为约束,any相当于空接口interface{})。Stack类型的底层是一个切片,其元素类型为T

并发

协程

Go 程(goroutine)是由 Go 运行时管理的轻量级线程。
go f(x, y, z)
会启动一个新的 Go 协程并执行
f(x, y, z)
f, x, yz 的求值发生在当前的 Go 协程中,而 f 的执行发生在新的 Go 协程中。
Go 程在相同的地址空间中运行,因此在访问共享的内存时必须进行同步。sync 包提供了这种能力,不过在 Go 中并不经常用到,因为还有其它的办法(见下一页)。

package mainimport ("fmt""time"
)func say(s string) {for i := 0; i < 5; i++ {time.Sleep(100 * time.Millisecond)fmt.Println(s)}
}func main() {go say("world")say("hello")
}

信道

信道是带有类型的管道,你可以通过它用信道操作符 <- 来发送或者接收值。

ch <- v    // 将 v 发送至信道 ch。
v := <-ch  // 从 ch 接收值并赋予 v。

(“箭头”就是数据流的方向。)
和映射与切片一样,信道在使用前必须创建:
ch := make(chan int)
默认情况下,发送和接收操作在另一端准备好之前都会阻塞。这使得 Go 程可以在没有显式的锁或竞态变量的情况下进行同步。
以下示例对切片中的数进行求和,将任务分配给两个 Go 程。一旦两个 Go 程完成了它们的计算,它就能算出最终的结果。

func sum(s []int, c chan int) {sum := 0for _, v := range s {sum += v}c <- sum // 发送 sum 到 c
}func main() {s := []int{7, 2, 8, -9, 4, 0}c := make(chan int)go sum(s[:len(s)/2], c)go sum(s[len(s)/2:], c)x, y := <-c, <-c // 从 c 接收fmt.Println(x, y, x+y) //-5 17 12
}
  1. 带缓冲的信道

信道可以是 带缓冲的。将缓冲长度作为第二个参数提供给 make 来初始化一个带缓冲的信道:

ch := make(chan int, 100)

仅当信道的缓冲区填满后,向其发送数据时才会阻塞。当缓冲区为空时,接受方会阻塞。
修改示例填满缓冲区,然后看看会发生什么。

range和close

发送者可通过 close 关闭一个信道来表示没有需要发送的值了。接收者可以通过为接收表达式分配第二个参数来测试信道是否被关闭:若没有值可以接收且信道已被关闭,那么在执行完
v, ok := <-ch
此时 ok 会被设置为 false
循环 for i := range c 会不断从信道接收值,直到它被关闭。
注意: 只应由发送者关闭信道,而不应油接收者关闭。向一个已经关闭的信道发送数据会引发程序 panic。
还要注意: 信道与文件不同,通常情况下无需关闭它们。只有在必须告诉接收者不再有需要发送的值时才有必要关闭,例如终止一个 range 循环。

package mainimport ("fmt"
)func fibonacci(n int, c chan int) {x, y := 0, 1for i := 0; i < n; i++ {c <- xx, y = y, x+y}close(c)
}func main() {c := make(chan int, 10)go fibonacci(cap(c), c)for i := range c {fmt.Println(i)}
}

select

select 语句使一个 Go 程可以等待多个通信操作。
select 会阻塞到某个分支可以继续执行为止,这时就会执行该分支。当多个分支都准备好时会随机选择一个执行。
select 中的其它分支都没有准备好时,default 分支就会执行。

package mainimport "fmt"func fibonacci(c, quit chan int) {x, y := 0, 1for {select {case c <- x:x, y = y, x+ycase <-quit:fmt.Println("quit")return}}
}func main() {c := make(chan int)quit := make(chan int)go func() {for i := 0; i < 10; i++ {fmt.Println(<-c)}quit <- 0}()fibonacci(c, quit)
}

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

QGIS 缓冲区交集信息提取

目标 计算出靠近河道的农田数量及位置&#xff0c;具体方法为使用QGIS 中计算出距离线图层&#xff08;代表河道&#xff09;100 米范围内的点&#xff08;代表水田&#xff09;图层中的点。 具体步骤 步骤 1: 创建缓冲区 首先需要基于线图层创建一个缓冲区图层。 打开 QGIS…

JavaScript基础——JavaScript调用的三种方式

JavaScript简介 JavaScript的作用 JavaScript的使用方式 内嵌JS 引入外部js文件 编写函数 JavaScript简介 JavaScript&#xff08;简称“JS”&#xff09;是一种具有函数优先的轻量级&#xff0c;解释型或即时编译型的编程语言。它是Web开发中最常用的脚本语言之一&#x…

软件测试:动态黑盒测试的过程

要成为一个成功的软件测试员&#xff0c;需要采用更结构化的、目标明确的方法继续测试。 本文粗略描述动态黑盒测试的结构化过程 目录 1.动态黑盒测试 拿到需求文档或产品说明书 定义测试用例 test-case 2. 通过性测试和时效性测试 3. 等价类划分 4. 数据测试 边界条件…

【Redis】 拓展:Redis - BigKey方案探讨

BigKey: 用户越多&#xff0c;redis数据越多&#xff0c;bigkey会使得缓存数据更大&#xff0c;网络带宽会被占用&#xff0c;执行效率就低下&#xff0c;高并发的时候吞吐量QPS也会下降。 产生原因&#xff1a; 看如下list&#xff1a; 一个key的内容太大&#xff0c;比如1M&…

【宝藏系列】物联网中常用的十种通信协议

【宝藏系列】物联网中常用的十种通信协议 文章目录 【宝藏系列】物联网中常用的十种通信协议1️⃣MQTT2️⃣CoAP3️⃣AMQP4️⃣XMPP5️⃣LwM2M6️⃣HTTP7️⃣DDS8️⃣Bluetooth Low Energy9️⃣LoRaWAN1️⃣0️⃣NB-IoT 1️⃣MQTT MQTT&#xff08;Message Queuing Telemetry T…

JNDI注入-高版本绕过

参考博客&#xff1a; JNDI注入与动态类加载 探索高版本 JDK 下 JNDI 漏洞的利用方法 - 跳跳糖 (tttang.com) 分析版本 jdk8u201 分析流程 修复 在ldap绕过中&#xff0c;我们讲了LDAP的修复&#xff0c;下面用jdk8u201具体来看下修复。 修复之前&#xff0c;利用是在L…

英文文献翻译方法哪个好?高效率的翻译方法分享

三伏天的酷热也抵挡不住学术人探索知识的脚步&#xff0c;阅读和翻译英文文献几乎已经成为了许多研究者和学者的日常。然而在面对浩如烟海的英文资料时&#xff0c;如何高效准确地进行翻译&#xff0c;成为了亟待解决的问题。 今天我便挖掘到了5款实用的英文文献翻译工具&…

【论文共读】【翻译】【GPT】Improving Language Understanding by Generative Pre-Training

GPT 原论文地址 翻译&#xff1a; Improving Language Understanding by Generative Pre-Training 通过生成式预训练提高语言理解能力 0. 摘要 自然语言理解包括各种不同的任务&#xff0c;例如文本蕴涵、问答、语义相似性评估和文档分类。尽管大量未标记的文本语料库很丰富…

《昇思25天学习打卡营第24天》

接续上一天的学习任务&#xff0c;我们要继续进行下一步的操作 构造网络 当处理完数据后&#xff0c;就可以来进行网络的搭建了。按照DCGAN论文中的描述&#xff0c;所有模型权重均应从mean为0&#xff0c;sigma为0.02的正态分布中随机初始化。 接下来了解一下其他内容 生成…

科普文:万字梳理高性能 Kafka快的8个原因

概叙 科普文&#xff1a;万字详解Kafka基本原理和应用-CSDN博客 科普文&#xff1a;万字梳理31个Kafka问题-CSDN博客 我们都知道 Kafka 是基于磁盘进行存储的&#xff0c;但 Kafka 官方又称其具有高性能、高吞吐、低延时的特点&#xff0c;其吞吐量动辄几十上百万。 在座的…

苹果safari历史记录如何恢复?4大秘籍,重访历史足迹

作为苹果设备上的默认浏览器&#xff0c;Safari为我们提供了便捷、快速的网页浏览体验。但是&#xff0c;如果出现意外删除或其他情况&#xff0c;我们可能会丢失Safari历史记录&#xff0c;这无疑给我们工作和学习带来了诸多不便。本文旨在帮助广大iPhone用户解决这一难题。通…

【音频识别】十大数据集合集,宝藏合集,不容错过!

本文将为您介绍10个经典、热门的数据集&#xff0c;希望对您在选择适合的数据集时有所帮助。 1 RenderMe-360 发布方&#xff1a; 上海人工智能实验室 发布时间&#xff1a; 2023-05-24 简介&#xff1a; RenFace是一个大规模多视角人脸高清视频数据集&#xff0c;包含多样的…

便携移动工作站,端侧 AI 大模型设备折腾笔记:ROG 幻 X 和 4090 扩展坞

为了本地测试和开发更丝滑&#xff0c;最近入手了一套新设备 ROG 幻 X Z13 和 ROG XG Mobile 4090 扩展坞。 基于这套设备&#xff0c;我搭了一套 Windows x WSL2 x CUDA 的开发环境。分享一下折腾记录&#xff0c;或许对有类似需求的你也有帮助。 写在前面 最近因为各种事情…

学习web前端三大件之HTML篇

HTML的全称为超文本标记语言&#xff0c;是一种标记语言。它包括一系列标签&#xff0c;通过这些标签可以将网络上的文档格式统一&#xff0c;使分散的Internet资源连接为一个逻辑整体。HTML文本是由HTML命令组成的描述性文本&#xff0c;HTML命令可以说明文字&#xff0c;图形…

单链表习题——快慢指针类习题详解!(2)

前言&#xff1a; 正如标题所言&#xff0c;小编今天要讲述快慢指针的相关习题&#xff0c;可能有些读者朋友会有些疑问了&#xff0c;这快慢指针是个什么东西&#xff1f;不要着急&#xff0c;下面紧跟小编的步伐&#xff0c;开启我们今天的快慢指针之旅&#xff01; 目录&…

安全基础学习-CRC理解与计算

由于一些任务要求需要了解CRC校验&#xff0c;于是来学习一下。 新人学习&#xff0c;大佬绕路。 前言 CRC即循环冗余校验码&#xff1a;是数据通信领域中最常用的一种查错校验码&#xff0c;其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查&#xff08;CRC&…

Seata 入门与实战

一、什么是 Seata Seata 是一款开源的分布式事务解决方式&#xff0c;致力于提供高性能和简单易用的分布式事务服务。Seata 为用户提供了 AT、TCC、SAGA 和 XA 事务模式&#xff0c;为用户打造一站式的分布式事务解决方案。 二、Seata 组成 事务协调者&#xff08;Transacti…

Potree点云可视化库在Vue项目中的应用

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 Potree点云可视化库在Vue项目中的应用 应用场景介绍 Potree是一个用于大规模点云渲染和交互的开源JavaScript库。它提供了高效的点云可视化和处理功能&#xff0c;广泛应用于地理信息系统&#xff08;GIS&…

整理几个常用的Linux命令(Centos发行版)

如果工作中需要经常整理一些文档&#xff0c;需要汇总一下&#xff0c;现有的服务器资源信息&#xff0c;那么这篇文章适合你&#xff1b; 如果你是一名开发者&#xff0c;需要经常登录服务器&#xff0c;排查应用的出现的一些问题&#xff0c;那么这篇文章适合你&#xff1b;…

《最新出炉》系列初窥篇-Python+Playwright自动化测试-61 - 隐藏元素定位与操作

软件测试微信群&#xff1a;https://bbs.csdn.net/topics/618423372 有兴趣的可以扫码加入 1.简介 对于前端隐藏元素&#xff0c;一直是自动化定位元素的隐形杀手&#xff0c;让人防不胜防。脚本跑到隐藏元素时位置时报各种各样的错误&#xff0c;可是这种隐藏的下拉菜单又没…