超实用的Go语言基础教程,让你快速上手!背景😎 工欲善其事,必先利其器。掌握Go的基础语法还不够,还需要勤加练习,修习“外功”,才能达到出奇制胜的效果。
在大致了解Go语言的基本语法后,我就迫不得已地想使用这门语言。可是我发现编程思路不是问题,很大的问题是“手慢”,不熟悉常用写法(可能这就是快速过语法的缺点吧,脑子会了,手没会)φ(* ̄0 ̄)。
在我看来,用Go语言刷算法题是一个非常好的练习“外功”的法门,可以帮助我提高思维的灵敏性和解决抽象化问题的能力。更重要地是复习我学习过的语法知识,不然真的很容易忘。虽然它和C语言有点像,但是我也并不经常使用C,两者不太好建立起清晰的关联图。因此,我会一边勤能补拙,一边总结一些语法知识,一边建立语言之间的联系,方便我加深记忆。
我刷的不是Leetcode形式的题目,而是ACM形式的题目。因为ACM形式需要处理输入输出,这对我的要求会更高点。
刷题平台:洛谷
基础知识🤔
输入处理 Go接收输入的方式有四类,分别是 fmt 包中的 Scan 、Scanf 和Scanln函数以及bufio.Scanner对象实现。
使用场景 :可以用于读取一段空格分隔 的字符串或多个数值类型的输入,例如读取数字或时间等;
示例一:计算浮点数相除的余。
输入格式:输入仅一行,包括两个双精度浮点数a和b。
输入样例:
处理方式:
1 2 3 4 5 6 7 8 func main () {var a, b float64 _, err := fmt.Scan(&a, &b)if err != nil { fmt.Println(err) } }
使用场景 :适用于需要按特定格式 读取和处理输入数据的场景,例如读取时间、日期、金额等;
示例二:数字排序
输入格式:输入三个数字,数字之间用逗号隔开。
输入样例:
处理方式:
1 2 3 4 5 6 7 8 9 10 11 package mainimport ("fmt" )func main () {var a, b, c int fmt.Scanf("%d,%d,%d" , &a, &b, &c) fmt.Println(a, b, c) }
如果输入不止三个数字,输入很长怎么办?
我想到的是直接当字符串保存,然后用“,”分割每一个元素,获得一个字符串数组,最后利用Atoi函数将字符串转为整数,存储到一个新的int类型数组中。
具体做法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package main import ("bufio" "fmt" "os" "strconv" "strings" ) func main () {var input string scanner := bufio.NewScanner (os.Stdin)if scanner.Scan () {input = scanner.Text () } else { fmt.Println ("Error" ) } strArray := strings.Split (input , "," ) intArray := make ([] int, len (strArray)) for i , v := range strArray {var err error intArray[i] , err = strconv.Atoi (strings.TrimSpace (v)) if err != nil { fmt.Println ("Error" ) } } fmt.Printf ("The input integers are: %v\n" , intArray) }
使用场景 :适用于读取空格或换行 分隔的字符串或多个数值类型的输入,例如读取单词或名称等。用法和Scan相似,就不举例子了。(~ ̄▽ ̄)~
使用场景 :这个对象可以从标准输入中逐行读取输入 ,直到遇到文件结尾或输入流关闭为止。特别适合循环读入数据!
示例三:字符串读取,并打印
输入格式:输入多行英文句子。
输入样例:
1 2 3 wow! you are pretty good at printing! you win.
处理方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package mainimport ("bufio" "fmt" "os" )func main () {var strArray []string scanner := bufio.NewScanner(os.Stdin)for scanner.Scan() { input := scanner.Text()if input == "" {break } strArray = append (strArray, input) }if err := scanner.Err(); err != nil { fmt.Printf("Error reading standard input: %s\n" , err.Error()) } fmt.Printf("Read %d lines:\n" , len (strArray))for i, line := range strArray { fmt.Printf("%d: %s\n" , i+1 , line) } }
输出处理 Go处理输出的方式根据场景的不同,可以分为以下几种:
终端或控制台中输出一些信息 ,使用fmt包中的函数。1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport ("fmt" )func main () { name := "Tom" age := 18 fmt.Println("name:" , name, "age:" , age) fmt.Printf("name: %s age: %d\n" , name, age) str1 := fmt.Sprintf("name: %s age: %d\n" , name, age) fmt.Printf(str1) }
记录程序运行过程中的日志信息 时,可以使用log包中的函数。1 2 3 4 5 6 7 8 9 10 11 12 package mainimport ("fmt" "log" )func main () { log.Println("Starting the application..." ) fmt.Println("Hello, World!" ) log.Println("Terminating the application..." ) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package main import ("fmt" "log" "os" ) func main() {file , err := os.Open ("test.txt" )if err != nil {log .Fatal(err ) } defer file .Close () buffer := make([]byte, 1024) for { bytesRead, err := file .Read (buffer) if err != nil {log .Fatal(err ) } fmt.Println("bytes read: " , bytesRead) fmt.Println("bytes:" , buffer[:bytesRead])if bytesRead < 1024 {break } } fmt.Printf("File contents: %s" , buffer) }
执行系统命令或创建进程 时,可以使用os包中的函数。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package main import ("fmt" "log" "os" "os/exec" ) func main () { cmd := exec.Command ("whoami" ) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err := cmd.Run ()if err != nil { log.Fatal (err) } fmt.Println ("Done" ) }
ACM形式的题目更多考察的是第一种在终端/控制台输出信息的格式。这个就要涉及到Go语言格式化字符串的方式的知识点。在我看来,格式化字符串在每种语言里都享有很高的地位。毕竟更美观的打印数据,也有助于我们更好的理解信息。
格式 描述 %v 表示按照值的默认格式输出,可以输出任意类型的数据。 %s 表示输出字符串类型的数据。 %d 表示输出十进制整数类型的数据。 %f 表示输出浮点数类型的数据。 %t 表示输出布尔类型的数据,true和false分别对应输出1和0。 %p 表示输出指针类型的数据。 %c 表示输出字符类型的数据。 %q 表示输出带引号的字符串类型的数据。 %b 表示输出二进制数类型的数据。 %x 表示输出十六进制数类型的数据。 %o 表示输出八进制数类型的数据。 %05d 表示输出5位,不足的位数用0补齐。 %.2f 表示输出小数点后两位。 %10s 输出10个字符长度,不足的位数用空格补齐
1 2 3 4 5 6 7 8 9 10 11 12 package mainimport "fmt" func main () { name := "Tom" age := 18 height := 1.75 fmt.Printf("My name is %s, I'm %d years old, and I'm %.2f meters tall.\n" , name, age, height) fmt.Printf("My name is %10s, I'm %05d years old, and I'm %.2f meters tall.\n" , name, age, height) }
1 2 My name is Tom, I'm 18 years old, and I'm 1.75 meters tall. My name is Tom, I'm 00018 years old, and I'm 1.75 meters tall.
数组?切片? 在Go语言中,数组是一种固定长度 的数据结构,一旦定义了数组的长度,就无法再向数组中添加新的元素。如果想动态更改,可以考虑使用切片。根据使用方法可以大致分个类:
共性 差异 下标访问 定义方式不同 循环遍历 切片可以添加/删除元素 长度计算 切片[start:end]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package mainimport ("fmt" )func main () {var n int fmt.Scan(&n)var arr []int var max int = -10000000 var sum int = 0 for i := 0 ; i < n; i++ {var x int fmt.Scan(&x) sum += xif max < x { max = x } arr = append (arr, x) }var count int = 0 for i := 0 ; i < n; i++ {if max == arr[i] { count++ } } fmt.Println(sum - max*count) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package mainimport ("fmt" )func main () {var n int fmt.Scan(&n)var used [110 ]int for i := 0 ; i < n; i++ {var x int fmt.Scan(&x) used[x]++if used[x] < 2 { fmt.Print(x, " " ) } } }
字符串处理 在Go语言中,字符串的长度是指字符串中字节的个数,而不是字符的个数。对于包含非ASCII字符的字符串,一个字符可能会占用多个字节。
1 2 3 4 5 6 7 8 9 10 11 12 package mainimport ("fmt" )func main () { str := "hello world" fmt.Println(len (str)) str = "hello 世界" fmt.Println(len (str)) }
既可以使用传统的下标遍历,也可以使用range遍历。建议使用range遍历,因为当字符串中出现中文时,下标遍历获取的是byte类型的值,也就意味着它是将一个汉字拆成了3个byte类型字节分别输出。
1 2 3 4 5 6 7 8 9 10 11 12 package mainimport ("fmt" )func main () { str := "hello world" for i, v := range str { fmt.Printf("字符串中下标为 %d 的字符是 %c\n" , i, v) } }
需要注意的是,在使用字符串切片时,下标是按字节计算的,而不是按字符 计算的。
1 2 3 str := "hello world" slice := str[1 :5 ] fmt.Println(slice)
可以使用加号运算符或fmt.Sprintf函数来连接字符串。
1 2 3 4 5 6 7 str1 := "hello" str2 := "world" str3 := str1 + " " + str2 fmt.Println(str3) str4 := fmt.Sprintf("%s %s" , str1, str2) fmt.Println(str4)
使用strings包中的函数来查找字符串中的子串。
1 2 3 str := "hello world" index := strings.Index(str, "world" ) fmt.Println(index)
使用strings包中的函数来替换字符串中的子串。
1 2 3 str := "hello world" newstr := strings.Replace(str, "world" , "golang" , -1 ) fmt.Println(newstr)
使用strconv包中的函数进行转换。
1 2 3 4 5 6 7 8 9 10 11 str := "123" num, err := strconv.Atoi(str) if err != nil { fmt.Println("转换失败" ) } else { fmt.Printf("转换结果是 %T\n" , num) } num = 123 str = strconv.Itoa(num) fmt.Printf("转换结果是 %T\n" , str)
预定义字符集 描述 \d 匹配一个数字字符。等价于字符集 [0-9]。 \s 匹配一个空白字符(空格、制表符、换行符等)。等价于字符集 [ \t\n\r\f\v]。 \w 匹配一个单词字符。等价于字符集 [a-zA-Z0-9_]。 \W 匹配一个非单词字符。等价于字符集 [^a-zA-Z0-9_]。 \S 匹配一个非空白字符。等价于字符集 [^ \t\n\r\f\v]。 \D 匹配一个非数字字符。等价于字符集 [^0-9]。 \b 表示单词边界,我的理解是能准确匹配到某个单词,不把包含这个单词的前缀词算在内。比如gotest就无法匹配test。
匹配一个由汉字组成的字符串(数据清洗时常用!):
匹配一个由邮箱地址组成的字符串(匹配恶意URL、匹配钓鱼邮箱常用):
演示1 :匹配一个字符串是否符合某个正则表达式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import ("fmt" "regexp" )func main () { pattern := "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$" reg := regexp.MustCompile(pattern) str := "abc123@11-2.com" matched := reg.MatchString(str) fmt.Println(matched) }
演示2 :利用正则进行查找和替换字符串
1 2 3 4 5 6 7 8 9 10 11 str := "hello world" re := regexp.MustCompile(`\b\w+o\w+\b` ) newstr := re.FindAllString(str, -1 ) fmt.Println(newstr) str := "hello world" re := regexp.MustCompile(`\b\w+o\w+\b` ) newstr := re.ReplaceAllString(str, "golang" ) fmt.Println(newstr)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package mainimport ("bufio" "fmt" "os" )func main () { scanner := bufio.NewScanner(os.Stdin) scanner.Scan() sentence := scanner.Text()var count int = 0 for _, v := range sentence {if v >= '0' && v <= '9' { count++ } } fmt.Println(count) }
结构体 Go语言的结构体和C语言很相似。
1 2 3 4 5 type Person struct { Name string Age int Height float32 }
1 p1 := Person{Name: "Alice" , Age: 20 , Height: 1.65 }
指针和普通的对象类型都是使用“.”号访问。
1 2 3 p1.Name = "Alice" p1.Age = 20 p1.Height = 1.65
分界线:————————————————————————————————————————————————————
Go还支持一些面向对象的编程特性,非常的灵活和强大!!!
1 2 3 4 5 func (p *Person) GetInfo() string { return fmt.Sprintf("Name: %s, Age: %d, Height: %.2f" , p.Name, p.Age, p.Height) } p1.GetInfo()
这个方法定义了一个指针类型为Person的方法GetInfo ,用来返回一个包含Person对象信息的字符串。我们可以通过调用结构体变量的方法来实现对结构体对象的操作。这种使用方法就很棒!这就有点像类方法,GetInfo函数就是Person结构体的类方法。想要使用这个方法,那么就需要先构造一个Person的结构体对象,然后通过对象调用。
此外,Go还支持封装、继承、多态的特性,用来实现复杂的对象模型和数据结构。
1 2 3 4 5 6 7 8 9 10 11 12 type Person struct { name string age int }func (p *Person) SetName(name string ) { p.name = name }func (p *Person) GetName() string { return p.name }
这个结构体定义了一个名为Person的结构体类型,包含了两个私有的成员变量name和age ,以及两个公有的方法SetName和GetName,用来设置和获取name成员变量的值。不同于其它语言使用Public,Private定义公有和私有,Go使用编程规范来定义这个概念。变量名首字母大写代表公有,对外可见;变量名首字母小写代表私有,对外不可见。 (经过实验,上面的说法是有一个大前提的。同一个包内,无论是公有变量还是私有变量,在任何地方都可以访问!!!! ,只有在不同的包 里,才有上面变量名大小写来控制可见性的说法。😣😣😣)Go的变量命名主要使用驼峰命名法,也算是约定俗成吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 type Person struct { name string age int }type Student struct { Person id string }func (s *Student) SetId(id string ) { s.id = id }
这个结构体定义了一个名为Student的结构体类型,通过匿名嵌套Person结构体,实现了从Person结构体继承了name和age成员变量和方法,并添加了一个id成员变量和SetId方法。这样,我们就可以通过Student结构体来访问和操作Person结构体的成员变量和方法。匿名嵌套是继承,不匿名就是组合的使用方法了。
声明一个Shape类型的接口,该接口里定义了Area()函数。Rectangle和Circle实现了Shape类型接口里的Area()的方法,可以认定为是一个实现类。PrintArea方法接受一个Shape类型的数据,然后输出面积。这个形参是Shape类型,因此,就有了一个“向上转型 ”的效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package mainimport ("fmt" "math" )type Shape interface { Area() float64 }type Rectangle struct { Width float64 Height float64 }func (r Rectangle) Area() float64 {return r.Width * r.Height }type Circle struct { Radius float64 }func (c Circle) Area() float64 {return math.Pi * c.Radius * c.Radius }func PrintArea (s Shape) { fmt.Println(s.Area()) }func main () { r := Rectangle{Width: 3 , Height: 4 } c := Circle{Radius: 5 } PrintArea(r) PrintArea(c) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 package mainimport ("fmt" "math" )type coordinate struct { x, y int isMarked bool }func distence (x1 int , y1 int , x2 int , y2 int ) float64 {return math.Sqrt(math.Pow(float64 (x1-x2), 2 ) + math.Pow(float64 (y1-y2), 2 )) }func main () {var n, k, t int fmt.Scan(&n, &k, &t) coordinates := make ([]coordinate, n)for i := 0 ; i < n; i++ { fmt.Scan(&coordinates[i].x, &coordinates[i].y) }for i := 0 ; i < k; i++ {var x, y int fmt.Scan(&x, &y)for j := 0 ; j < n; j++ {if x == coordinates[j].x && y == coordinates[j].y { coordinates[j].isMarked = true break } } }var maxDistence float64 = 0.0 var maxDistenceid int = -1 var res int = 0 for i := 0 ; i < t; i++ {var x, y int fmt.Scan(&x, &y)for j := 0 ; j < n; j++ {if distence(x, y, coordinates[j].x, coordinates[j].y) > maxDistence { maxDistence = distence(x, y, coordinates[j].x, coordinates[j].y) maxDistenceid = j } }if coordinates[maxDistenceid].isMarked { res++ } maxDistence = 0.0 maxDistenceid = -1 } fmt.Println(res) }
]]>
+ 超实用的Go语言基础教程,让你快速上手!背景😎 工欲善其事,必先利其器。掌握Go的基础语法还不够,还需要勤加练习,修习“外功”,才能达到出奇制胜的效果。
在大致了解Go语言的基本语法后,我就迫不得已地想使用这门语言。可是我发现编程思路不是问题,很大的问题是“手慢”,不熟悉常用写法(可能这就是快速过语法的缺点吧,脑子会了,手没会)φ(* ̄0 ̄)。
在我看来,用Go语言刷算法题是一个非常好的练习“外功”的法门,可以帮助我提高思维的灵敏性和解决抽象化问题的能力。更重要地是复习我学习过的语法知识,不然真的很容易忘。虽然它和C语言有点像,但是我也并不经常使用C,两者不太好建立起清晰的关联图。因此,我会一边勤能补拙,一边总结一些语法知识,一边建立语言之间的联系,方便我加深记忆。
我刷的不是Leetcode形式的题目,而是ACM形式的题目。因为ACM形式需要处理输入输出,这对我的要求会更高点。
刷题平台:洛谷
基础知识🤔
输入处理 Go接收输入的方式有四类,分别是 fmt 包中的 Scan 、Scanf 和Scanln函数以及bufio.Scanner对象实现。
使用场景 :可以用于读取一段空格分隔 的字符串或多个数值类型的输入,例如读取数字或时间等;
示例一:计算浮点数相除的余。
输入格式:输入仅一行,包括两个双精度浮点数a和b。
输入样例:
处理方式:
1 2 3 4 5 6 7 8 func main () {var a, b float64 _, err := fmt.Scan(&a, &b)if err != nil { fmt.Println(err) } }
使用场景 :适用于需要按特定格式 读取和处理输入数据的场景,例如读取时间、日期、金额等;
示例二:数字排序
输入格式:输入三个数字,数字之间用逗号隔开。
输入样例:
处理方式:
1 2 3 4 5 6 7 8 9 10 11 package mainimport ("fmt" )func main () {var a, b, c int fmt.Scanf("%d,%d,%d" , &a, &b, &c) fmt.Println(a, b, c) }
如果输入不止三个数字,输入很长怎么办?
我想到的是直接当字符串保存,然后用“,”分割每一个元素,获得一个字符串数组,最后利用Atoi函数将字符串转为整数,存储到一个新的int类型数组中。
具体做法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package main import ("bufio" "fmt" "os" "strconv" "strings" ) func main () {var input string scanner := bufio.NewScanner (os.Stdin)if scanner.Scan () {input = scanner.Text () } else { fmt.Println ("Error" ) } strArray := strings.Split (input , "," ) intArray := make ([] int, len (strArray)) for i , v := range strArray {var err error intArray[i] , err = strconv.Atoi (strings.TrimSpace (v)) if err != nil { fmt.Println ("Error" ) } } fmt.Printf ("The input integers are: %v\n" , intArray) }
使用场景 :适用于读取空格或换行 分隔的字符串或多个数值类型的输入,例如读取单词或名称等。用法和Scan相似,就不举例子了。(~ ̄▽ ̄)~
使用场景 :这个对象可以从标准输入中逐行读取输入 ,直到遇到文件结尾或输入流关闭为止。特别适合循环读入数据!
示例三:字符串读取,并打印
输入格式:输入多行英文句子。
输入样例:
1 2 3 wow! you are pretty good at printing! you win.
处理方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package mainimport ("bufio" "fmt" "os" )func main () {var strArray []string scanner := bufio.NewScanner(os.Stdin)for scanner.Scan() { input := scanner.Text()if input == "" {break } strArray = append (strArray, input) }if err := scanner.Err(); err != nil { fmt.Printf("Error reading standard input: %s\n" , err.Error()) } fmt.Printf("Read %d lines:\n" , len (strArray))for i, line := range strArray { fmt.Printf("%d: %s\n" , i+1 , line) } }
输出处理 Go处理输出的方式根据场景的不同,可以分为以下几种:
终端或控制台中输出一些信息 ,使用fmt包中的函数。1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport ("fmt" )func main () { name := "Tom" age := 18 fmt.Println("name:" , name, "age:" , age) fmt.Printf("name: %s age: %d\n" , name, age) str1 := fmt.Sprintf("name: %s age: %d\n" , name, age) fmt.Printf(str1) }
记录程序运行过程中的日志信息 时,可以使用log包中的函数。1 2 3 4 5 6 7 8 9 10 11 12 package mainimport ("fmt" "log" )func main () { log.Println("Starting the application..." ) fmt.Println("Hello, World!" ) log.Println("Terminating the application..." ) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package main import ("fmt" "log" "os" ) func main() {file , err := os.Open ("test.txt" )if err != nil {log .Fatal(err ) } defer file .Close () buffer := make([]byte, 1024) for { bytesRead, err := file .Read (buffer) if err != nil {log .Fatal(err ) } fmt.Println("bytes read: " , bytesRead) fmt.Println("bytes:" , buffer[:bytesRead])if bytesRead < 1024 {break } } fmt.Printf("File contents: %s" , buffer) }
执行系统命令或创建进程 时,可以使用os包中的函数。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package main import ("fmt" "log" "os" "os/exec" ) func main () { cmd := exec.Command ("whoami" ) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err := cmd.Run ()if err != nil { log.Fatal (err) } fmt.Println ("Done" ) }
ACM形式的题目更多考察的是第一种在终端/控制台输出信息的格式。这个就要涉及到Go语言格式化字符串的方式的知识点。在我看来,格式化字符串在每种语言里都享有很高的地位。毕竟更美观的打印数据,也有助于我们更好的理解信息。
格式 描述 %v 表示按照值的默认格式输出,可以输出任意类型的数据。 %s 表示输出字符串类型的数据。 %d 表示输出十进制整数类型的数据。 %f 表示输出浮点数类型的数据。 %t 表示输出布尔类型的数据,true和false分别对应输出1和0。 %p 表示输出指针类型的数据。 %c 表示输出字符类型的数据。 %q 表示输出带引号的字符串类型的数据。 %b 表示输出二进制数类型的数据。 %x 表示输出十六进制数类型的数据。 %o 表示输出八进制数类型的数据。 %05d 表示输出5位,不足的位数用0补齐。 %.2f 表示输出小数点后两位。 %10s 输出10个字符长度,不足的位数用空格补齐
1 2 3 4 5 6 7 8 9 10 11 12 package mainimport "fmt" func main () { name := "Tom" age := 18 height := 1.75 fmt.Printf("My name is %s, I'm %d years old, and I'm %.2f meters tall.\n" , name, age, height) fmt.Printf("My name is %10s, I'm %05d years old, and I'm %.2f meters tall.\n" , name, age, height) }
1 2 My name is Tom, I'm 18 years old, and I'm 1.75 meters tall. My name is Tom, I'm 00018 years old, and I'm 1.75 meters tall.
数组?切片? 在Go语言中,数组是一种固定长度 的数据结构,一旦定义了数组的长度,就无法再向数组中添加新的元素。如果想动态更改,可以考虑使用切片。根据使用方法可以大致分个类:
共性 差异 下标访问 定义方式不同 循环遍历 切片可以添加/删除元素 长度计算 切片[start:end]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package mainimport ("fmt" )func main () {var n int fmt.Scan(&n)var arr []int var max int = -10000000 var sum int = 0 for i := 0 ; i < n; i++ {var x int fmt.Scan(&x) sum += xif max < x { max = x } arr = append (arr, x) }var count int = 0 for i := 0 ; i < n; i++ {if max == arr[i] { count++ } } fmt.Println(sum - max*count) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package mainimport ("fmt" )func main () {var n int fmt.Scan(&n)var used [110 ]int for i := 0 ; i < n; i++ {var x int fmt.Scan(&x) used[x]++if used[x] < 2 { fmt.Print(x, " " ) } } }
字符串处理 在Go语言中,字符串的长度是指字符串中字节的个数,而不是字符的个数。对于包含非ASCII字符的字符串,一个字符可能会占用多个字节。
1 2 3 4 5 6 7 8 9 10 11 12 package mainimport ("fmt" )func main () { str := "hello world" fmt.Println(len (str)) str = "hello 世界" fmt.Println(len (str)) }
既可以使用传统的下标遍历,也可以使用range遍历。建议使用range遍历,因为当字符串中出现中文时,下标遍历获取的是byte类型的值,也就意味着它是将一个汉字拆成了3个byte类型字节分别输出。
1 2 3 4 5 6 7 8 9 10 11 12 package mainimport ("fmt" )func main () { str := "hello world" for i, v := range str { fmt.Printf("字符串中下标为 %d 的字符是 %c\n" , i, v) } }
需要注意的是,在使用字符串切片时,下标是按字节计算的,而不是按字符 计算的。
1 2 3 str := "hello world" slice := str[1 :5 ] fmt.Println(slice)
可以使用加号运算符或fmt.Sprintf函数来连接字符串。
1 2 3 4 5 6 7 str1 := "hello" str2 := "world" str3 := str1 + " " + str2 fmt.Println(str3) str4 := fmt.Sprintf("%s %s" , str1, str2) fmt.Println(str4)
使用strings包中的函数来查找字符串中的子串。
1 2 3 str := "hello world" index := strings.Index(str, "world" ) fmt.Println(index)
使用strings包中的函数来替换字符串中的子串。
1 2 3 str := "hello world" newstr := strings.Replace(str, "world" , "golang" , -1 ) fmt.Println(newstr)
使用strconv包中的函数进行转换。
1 2 3 4 5 6 7 8 9 10 11 str := "123" num, err := strconv.Atoi(str) if err != nil { fmt.Println("转换失败" ) } else { fmt.Printf("转换结果是 %T\n" , num) } num = 123 str = strconv.Itoa(num) fmt.Printf("转换结果是 %T\n" , str)
预定义字符集 描述 \d 匹配一个数字字符。等价于字符集 [0-9]。 \s 匹配一个空白字符(空格、制表符、换行符等)。等价于字符集 [ \t\n\r\f\v]。 \w 匹配一个单词字符。等价于字符集 [a-zA-Z0-9_]。 \W 匹配一个非单词字符。等价于字符集 [^a-zA-Z0-9_]。 \S 匹配一个非空白字符。等价于字符集 [^ \t\n\r\f\v]。 \D 匹配一个非数字字符。等价于字符集 [^0-9]。 \b 表示单词边界,我的理解是能准确匹配到某个单词,不把包含这个单词的前缀词算在内。比如gotest就无法匹配test。
匹配一个由汉字组成的字符串(数据清洗时常用!):
匹配一个由邮箱地址组成的字符串(匹配恶意URL、匹配钓鱼邮箱常用):
演示1 :匹配一个字符串是否符合某个正则表达式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import ("fmt" "regexp" )func main () { pattern := "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$" reg := regexp.MustCompile(pattern) str := "abc123@11-2.com" matched := reg.MatchString(str) fmt.Println(matched) }
演示2 :利用正则进行查找和替换字符串
1 2 3 4 5 6 7 8 9 10 11 str := "hello world" re := regexp.MustCompile(`\b\w+o\w+\b` ) newstr := re.FindAllString(str, -1 ) fmt.Println(newstr) str := "hello world" re := regexp.MustCompile(`\b\w+o\w+\b` ) newstr := re.ReplaceAllString(str, "golang" ) fmt.Println(newstr)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package mainimport ("bufio" "fmt" "os" )func main () { scanner := bufio.NewScanner(os.Stdin) scanner.Scan() sentence := scanner.Text()var count int = 0 for _, v := range sentence {if v >= '0' && v <= '9' { count++ } } fmt.Println(count) }
结构体 Go语言的结构体和C语言很相似。
1 2 3 4 5 type Person struct { Name string Age int Height float32 }
1 p1 := Person{Name: "Alice" , Age: 20 , Height: 1.65 }
指针和普通的对象类型都是使用“.”号访问。
1 2 3 p1.Name = "Alice" p1.Age = 20 p1.Height = 1.65
分界线:————————————————————————————————————————————————————
Go还支持一些面向对象的编程特性,非常的灵活和强大!!!
1 2 3 4 5 func (p *Person) GetInfo() string { return fmt.Sprintf("Name: %s, Age: %d, Height: %.2f" , p.Name, p.Age, p.Height) } p1.GetInfo()
这个方法定义了一个指针类型为Person的方法GetInfo ,用来返回一个包含Person对象信息的字符串。我们可以通过调用结构体变量的方法来实现对结构体对象的操作。这种使用方法就很棒!这就有点像类方法,GetInfo函数就是Person结构体的类方法。想要使用这个方法,那么就需要先构造一个Person的结构体对象,然后通过对象调用。
此外,Go还支持封装、继承、多态的特性,用来实现复杂的对象模型和数据结构。
1 2 3 4 5 6 7 8 9 10 11 12 type Person struct { name string age int }func (p *Person) SetName(name string ) { p.name = name }func (p *Person) GetName() string { return p.name }
这个结构体定义了一个名为Person的结构体类型,包含了两个私有的成员变量name和age ,以及两个公有的方法SetName和GetName,用来设置和获取name成员变量的值。不同于其它语言使用Public,Private定义公有和私有,Go使用编程规范来定义这个概念。变量名首字母大写代表公有,对外可见;变量名首字母小写代表私有,对外不可见。 (经过实验,上面的说法是有一个大前提的。同一个包内,无论是公有变量还是私有变量,在任何地方都可以访问!!!! ,只有在不同的包 里,才有上面变量名大小写来控制可见性的说法。😣😣😣)Go的变量命名主要使用驼峰命名法,也算是约定俗成吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 type Person struct { name string age int }type Student struct { Person id string }func (s *Student) SetId(id string ) { s.id = id }
这个结构体定义了一个名为Student的结构体类型,通过匿名嵌套Person结构体,实现了从Person结构体继承了name和age成员变量和方法,并添加了一个id成员变量和SetId方法。这样,我们就可以通过Student结构体来访问和操作Person结构体的成员变量和方法。匿名嵌套是继承,不匿名就是组合的使用方法了。
声明一个Shape类型的接口,该接口里定义了Area()函数。Rectangle和Circle实现了Shape类型接口里的Area()的方法,可以认定为是一个实现类。PrintArea方法接受一个Shape类型的数据,然后输出面积。这个形参是Shape类型,因此,就有了一个“向上转型 ”的效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package mainimport ("fmt" "math" )type Shape interface { Area() float64 }type Rectangle struct { Width float64 Height float64 }func (r Rectangle) Area() float64 {return r.Width * r.Height }type Circle struct { Radius float64 }func (c Circle) Area() float64 {return math.Pi * c.Radius * c.Radius }func PrintArea (s Shape) { fmt.Println(s.Area()) }func main () { r := Rectangle{Width: 3 , Height: 4 } c := Circle{Radius: 5 } PrintArea(r) PrintArea(c) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 package mainimport ("fmt" "math" )type coordinate struct { x, y int isMarked bool }func distence (x1 int , y1 int , x2 int , y2 int ) float64 {return math.Sqrt(math.Pow(float64 (x1-x2), 2 ) + math.Pow(float64 (y1-y2), 2 )) }func main () {var n, k, t int fmt.Scan(&n, &k, &t) coordinates := make ([]coordinate, n)for i := 0 ; i < n; i++ { fmt.Scan(&coordinates[i].x, &coordinates[i].y) }for i := 0 ; i < k; i++ {var x, y int fmt.Scan(&x, &y)for j := 0 ; j < n; j++ {if x == coordinates[j].x && y == coordinates[j].y { coordinates[j].isMarked = true break } } }var maxDistence float64 = 0.0 var maxDistenceid int = -1 var res int = 0 for i := 0 ; i < t; i++ {var x, y int fmt.Scan(&x, &y)for j := 0 ; j < n; j++ {if distence(x, y, coordinates[j].x, coordinates[j].y) > maxDistence { maxDistence = distence(x, y, coordinates[j].x, coordinates[j].y) maxDistenceid = j } }if coordinates[maxDistenceid].isMarked { res++ } maxDistence = 0.0 maxDistenceid = -1 } fmt.Println(res) }
]]>