Go(一)基础知识

一、第一个程序

基本程序结构

package main // 包

import "fmt" // 引入依赖代码

// 功能实现
func main() { fmt.Println("hello world!") }

注意点:

1.应用程序入口

  • 必须是main包:package main
  • 必须是main方法:func main()
  • 文件名不一定是main.go

2.退出返回值

与其他主要编程语言的差异

  • Go中main函数不支持任何返回值
  • 通过os.Exit来返回状态

3.获取命令行参数

main函数不支持传入参数

func main()

在程序中直接通过os.Args获取命令行参数

编写测试文件

源码文件以_test结尾:xxx_test.go

测试方法名以Test开头:func TestXXX(t *testing.T){...}

package try_test

import "testing"

func TestFirstTry(t *testing.T)  {

    t.Log("My first try!")

}

结果

=== RUN   TestFirstTry
--- PASS: TestFirstTry (0.00s)
    first_test.go:7: My first try!
PASS

Fibonacci数列

package try

import (
    "fmt"
    "testing"
)

func TestFibList(t *testing.T)  {
    var a int = 1
    var b int = 1

    //var{
    // 可以省略int,类型推断
    //    a int = 1
    //    b int = 1
    //}
    // 类型推断,初试化
    //a := 1
    //b := 1
    fmt.Print(a, " ")
    for i:=0;i<5;i++{
        fmt.Print(" ", b)
        tmp:=a
        a=b
        b=tmp+a
    }
    fmt.Println()
}

二、变量、常量、数据类型和运算符

变量赋值

与其他主要编程语言的差异

赋值可以进行自动推断

在一个赋值语句中可以对多个变量进行同时赋值

func TestExchage(t *testing.T){
    a := 1
    b := 1
    //tmp := a
    //a = b
    //b = tmp
    a, b= b, a
    t.Log(a, b)
}

常量定义

与其他主要编程语言的差异

快速设置连续值

const (
    Monday = iota + 1
    Tuesday
    Wednesday
    Thrusday
    Friday
    Saturday
    Sunday
)

const (
    Open = 1 << iota
    Close
    Pending
) 

例子

package _const

import "testing"

const (
    Monday = iota + 1
    Tuesday
    Wednesday
)

const(
    Readable = 1<<iota
    Writable
    Executable
)

func TestConstantTry(t *testing.T)  {
    t.Log(Monday, Tuesday, Wednesday)
} // 1,2,3

func TestConstantTry1(t *testing.T)  {
    a := 7 // 0111
    t.Log(a&Readable==Readable, a&Writable==Writable, a&Executable==Executable)
} // true,true,true

数据类型

基本数据类型

类型转化

与其他主要编程语言的差异

1.Go语言不允许隐式类型转换

2.别名和原有类型也不能进行隐式类型转换

package type_test

import "testing"

type MyInt int64

func TestImplict(t *testing.T)  {
    var a int = 1
    var b int64
    //b = a // 不支持隐式类型转换
    b = int64(a)
    t.Log(a, b)
    var c MyInt
    // c = b // 别名的隐式类型转换也不支持
    c = MyInt(b)
    t.Log(a, b, c)
}

类型的预定义值

指针类型

与其他主要编程语言的差异

1.不支持指针运算

func TestPoint(t *testing.T)  {
    a := 1
    aPtr := &a
    t.Log(a, aPtr)
    t.Logf("%T %T", a, aPtr)
    //aPtr = aPtr + 1 // 出错,不支持指针运算
}


结果

=== RUN TestPoint
--- PASS: TestPoint (0.00s)
type_test.go:22: 1 0xc04205e290
type_test.go:23: int *int
PASS

2.string是值类型,其默认初试化值为空字符串,而不是nil

func TestString(t *testing.T)  {
    var s string
    t.Log("*"+s+"*")
    t.Log(len(s))
}

结果
=== RUN   TestString
--- PASS: TestString (0.00s)
    type_test.go:29: **
    type_test.go:30: 0
PASS

运算符

算术运算符

比较运算符

用==比较数组

  • 相同维数且含有相同个数元素的数组才可以比较
  • 每个元素都相同的才相等

逻辑运算符

位运算符

与其他主要编程语言的差异

三、编写结构化程序

for循环

和其他主要编程语言的差异

 示例

实例

func TestWhileLoop(t *testing.T)  {
    n := 0
    for n < 5{
        t.Log(n)
        n++
    }
}


结果
=== RUN   TestWhileLoop
--- PASS: TestWhileLoop (0.00s)
    type_test.go:36: 0
    type_test.go:36: 1
    type_test.go:36: 2
    type_test.go:36: 3
    type_test.go:36: 4
PASS

if条件

 与其他编程语言的差异

  • condition表达式结果必须为布尔值
  • 支持变量赋值
if var declaration; condition {
    // code to be executed if condition is true
}
func TestIfMultiSec(t *testing.T)  {
    if a := 1 == 1;a {
        t.Log("1==1")
    }
}

// a是true,所以执行括号里内容


结果
=== RUN   TestIfMultiSec
--- PASS: TestIfMultiSec (0.00s)
    type_test.go:43: 1==1
PASS

 switch条件

 与其他主要编程语言的差异

  • 条件表达式不限制为常量或整数
  • 单个case中,可以出现多个结果选项,使用逗号分隔
  • 与C等规则相反,Go语言不需要用break来明确退出一个case
  • 可以不设定switch之后的条件表达式,在此情况下,整个switch结构与多个if...else...的逻辑作用等同

可以有多个选项

func TestSwitchMultiCase(t *testing.T)  {
    for i := 0; i < 5; i++{
        switch i {
        case 0, 2:
            t.Log("Even")
        case 1,3:
            t.Log("Odd")
        default:
            t.Log("It is not 0-3")
        }
    }
}

相当于if...else...使用

func TestSwitchCaseCondition(t *testing.T)  {
    for i := 0; i < 5; i++ {
        switch  {
        case i%2 == 0:
            t.Log("Even")
        case i%2 == 1:
            t.Log("Odd")
        default:
            t.Log("unknow")
        }
    }
}

四、数组和切片

 数组的声明

 数组元素遍历

与其他主要编程语言的差异

数组截取

切片内部结构

切片声明

切片共享存储结构

数组vs切片

  • 容量是否可伸缩
  • 是否可以进行比较

五、Map

 Map声明

Map元素访问

与其他主要编程语言的差异

Map遍历

 Map与工厂模式

  • Map的value可以是一个方法
  • 与Go的Dock type接口方式一起,可以方便实现单一方法对象的工厂模式

实现Set

 

六、字符串

 与其他主要编程语言的差异

 Unicode UTF8

编码与存储

常用字符串函数

package main

import (
    "fmt"
    "strings"
)

func main() {
    var s string = "hello go"
    //判断字符串s是否包含子串
    r := strings.Contains(s, "go")
    fmt.Println(r) //true

    var s1 []string = []string{"1", "2", "3", "4"}
    //将一系列字符串连接为一个字符串,之间用sep来分隔
    s2 := strings.Join(s1, ",")
    fmt.Println(s2) //1,2,3,4

    //子串sep在字符串s中第一次出现的位置,不存在则返回-1
    n1 := strings.Index(s, "go")
    fmt.Println(n1) //6

    //返回count个s串联的字符串
    s3 := strings.Repeat(s, 2)
    fmt.Println(s3) //hello gohello go

    //返回将s中前n个不重叠old子串都替换为new的新字符串,如果n<0会替换所有old子串
    s4 := strings.Replace(s, "o", "e", -1)
    fmt.Println(s4) //helle ge

    //字符串切割,如果后一个参数为空,则按字符切割,返回字符串切片
    s5 := strings.Split(s, " ")
    fmt.Println(s5) //[hello go]

    //切除字符串两端的某类字符串
    s6 := strings.Trim(s, "o")
    fmt.Println(s6) //hello g

    //去除字符串的空格符,并且按照空格分割返回slice
    s7 := " hello go "
    s8 := strings.Fields(s7)
    fmt.Println(s8) //[hello go]
}

 七、文件读写

文件读写

创建文件 Ceate :文件不存在创建,文件存在,将文件内容清空

    参数:name,打开文件的路径,可以是绝对路径和相对路径

func main() {

    file, err := os.Create("./hello.txt")
    if err != nil {
        fmt.Println("Create error:", err)
        return
    }
    defer file.Close()

    fmt.Println("succes!")

}

打开文件 Open :以只读方式打开文件,文件不存在也会打开失败

    参数:name,打开文件的路径,可以是绝对路径和相对路径

package main

import (
        "fmt";
         "os"
)

func main() {

    file, err := os.Open("./hello.txt")
    if err != nil {
        fmt.Println("Open error:", err)
        return
    }
    defer file.Close()

    _, err = file.WriteString("hello")
    if err != nil {
        fmt.Println("Write error:", err)
        return
    }

    fmt.Println("succes!")

}

结果

Write error: write ./hello.txt: Access is denied.

打开文件 OpenFile:以只读、只写、读写方式打开文件

    参数1:name,打开文件的路径,可以是绝对路径和相对路径

    参数2:打开文件权限,O_RDONLY、O_WRONLY、O_RDWR

    参数3:一般传6

const (
O_RDONLY int = syscall.O_RDONLY // 只读模式打开文件
O_WRONLY int = syscall.O_WRONLY // 只写模式打开文件
O_RDWR int = syscall.O_RDWR // 读写模式打开文件
O_APPEND int = syscall.O_APPEND // 写操作时将数据附加到文件尾部
O_CREATE int = syscall.O_CREAT // 如果不存在将创建一个新文件
O_EXCL int = syscall.O_EXCL // 和O_CREATE配合使用,文件必须不存在
O_SYNC int = syscall.O_SYNC // 打开文件用于同步I/O
O_TRUNC int = syscall.O_TRUNC // 如果可能,打开时清空文件
)

package main

import (
        "fmt";
         "os"
)

func main() {

    file, err := os.OpenFile("./hello.txt", os.O_RDWR, 6)
    if err != nil {
        fmt.Println("OpenFile error:", err)
        return
    }
    defer file.Close()

    _, err = file.WriteString("hello")
    if err != nil {
        fmt.Println("Write error:", err)
        return
    }

    fmt.Println("succes!")

}

结果

succes!

写文件

按字符串写:WriteString(string str)

_, err = file.WriteString("hello")

回车换行 Windows为 Linux为

按位置写:

    Seek():修改文件的读写指针位置

func (f *File) Seek(offset int64, whence int) (ret int64, err error) {
    if err := f.checkValid("seek"); err != nil {
        return 0, err
    }
    r, e := f.seek(offset, whence)
    if e == nil && f.dirinfo != nil && r != 0 {
        e = syscall.EISDIR
    }
    if e != nil {
        return 0, f.wrapErr("seek", e)
    }
    return r, nil
}

    参数1: 偏移量。正是向文件尾偏,负是向文件头偏

    参数2:偏移起始位置

                io.SeekStart:文件起始的位置 0

                io.SeekCurrent:文件当前位置 1

                io.SeekEnd:文件结尾位置 2

    返回值:表示从文件起始位置到当前文件读写指针位置的偏移量

按字节写:

    writeAt():在文件指定偏移位置,写入[]byte,通常搭配Seek()

    参数1:带写入的数据

    参数2:偏移量

    返回值:实际写出的字节数

package main

import (
    "fmt"
    "io"
    "os"
)

func main() {

    file, err := os.OpenFile("./hello.txt", os.O_RDWR, 6)
    if err != nil {
        fmt.Println("OpenFile error:", err)
        return
    }
    defer file.Close()

    _, err = file.WriteString("hello")
    if err != nil {
        fmt.Println("Write error:", err)
        return
    }


    off, _ := file.Seek(0, io.SeekStart)
    fmt.Println(off)  // 0

    off, _ = file.Seek(1, io.SeekCurrent)
    fmt.Println(off)  // 1

    off, _ = file.Seek(0, io.SeekEnd)
    fmt.Println(off)  // 5

    off, _ = file.Seek(-1, io.SeekEnd)
    fmt.Println(off) // 4

    off, _ = file.Seek(-2, io.SeekEnd)
    fmt.Println(off) // 3

    n, _ := file.WriteAt([]byte("111"), off)
    fmt.Println(n) //3
    
    fmt.Println("succes!")

}

文件 hello ---> hel111,偏移量3的地方开始写:

读文件

// Reader implements buffering for an io.Reader object.
type Reader struct {
    buf          []byte
    rd           io.Reader // reader provided by the client
    r, w         int       // buf read and write positions
    err          error
    lastByte     int // last byte read for UnreadByte; -1 means invalid
    lastRuneSize int // size of last rune read for UnreadRune; -1 means invalid
}
func NewReaderSize(rd io.Reader, size int) *Reader {
    // Is it already a Reader?
    b, ok := rd.(*Reader)
    if ok && len(b.buf) >= size {
        return b
    }
    if size < minReadBufferSize {
        size = minReadBufferSize
    }
    r := new(Reader)
    r.reset(make([]byte, size), rd)
    return r
}

// NewReader returns a new Reader whose buffer has the default size.
func NewReader(rd io.Reader) *Reader {
    return NewReaderSize(rd, defaultBufSize)
}

NewReader 返回的是结构体指针变量

// ReadBytes reads until the first occurrence of delim in the input,
// returning a slice containing the data up to and including the delimiter.
// If ReadBytes encounters an error before finding a delimiter,
// it returns the data read before the error and the error itself (often io.EOF).
// ReadBytes returns err != nil if and only if the returned data does not end in
// delim.
// For simple uses, a Scanner may be more convenient.
func (b *Reader) ReadBytes(delim byte) ([]byte, error) {
    // Use ReadSlice to look for array,
    // accumulating full buffers.
    var frag []byte
    var full [][]byte
    var err error
    n := 0
    for {
        var e error
        frag, e = b.ReadSlice(delim)
        if e == nil { // got final fragment
            break
        }
        if e != ErrBufferFull { // unexpected error
            err = e
            break
        }

        // Make a copy of the buffer.
        buf := make([]byte, len(frag))
        copy(buf, frag)
        full = append(full, buf)
        n += len(buf)
    }

    n += len(frag)

    // Allocate new buffer to hold the full pieces and the fragment.
    buf := make([]byte, n)
    n = 0
    // Copy full pieces and fragment in.
    for i := range full {
        n += copy(buf[n:], full[i])
    }
    copy(buf[n:], frag)
    return buf, err
}

按行读:

package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
)

func main() {

    file, err := os.OpenFile("./hello.txt", os.O_RDWR, 6)
    if err != nil {
        fmt.Println("OpenFile error:", err)
        return
    }
    defer file.Close()

    fmt.Println("succes!")

    // 创建一个带缓冲区的reader
    reader := bufio.NewReader(file)

    for{
        buf, err := reader.ReadBytes('
') // 读一行
        if err != nil && err == io.EOF{
            fmt.Println("文件读取完毕")
            return
        }else if err != nil {
            fmt.Println("ReadBytes error:", err)
        }
        fmt.Print(string(buf))
    }

}

按字节读、写文件

最常用

read()

write()

大文件拷贝

package main

import (
    "fmt"
    "io"
    "os"
)

func main() {

    file_r, err := os.Open("E:/文档/xmind/C#.png")
    if err != nil {
        fmt.Println("Open err: ", err)
        return
    }
    defer file_r.Close()
    // 创建写文件
    file_w, err := os.Create("./C#.png")
    if err != nil {
        fmt.Println("Create err: ", err)
        return
    }
    defer file_w.Close()

    // 从读文件中读取数据,放到缓冲区中
    buf := make([]byte, 4096)
    // 循环,从读文件中读取数据,拷贝都写文件中
    for  {
        n, err := file_r.Read(buf)
        if err != nil && err == io.EOF {
            fmt.Printf("已经读完 n=%d
", n)
            return
        }
        file_w.Write(buf[: n]) // 读多少,写多少
    }
}

结果

已经读完 n=0

因此,我们也可以通过n=0来判断文件读取完毕

目录操作

打开目录,和打开目录是一样的

目录中的内容叫目录项

打开目录 OpenFile:以只读、只写、读写方式打开目录

    参数1:name,打开文件的路径,可以是绝对路径和相对路径

    参数2:打开文件权限,O_RDONLY、O_WRONLY、O_RDWR

    参数3:os.ModeDir

    返回值:返回一个可以读写目录的文件指针

读目录

func (f *File) Readdir(n int) ([]FileInfo, error) {
    if f == nil {
        return nil, ErrInvalid
    }
    return f.readdir(n)
}

    参数:n,表示读取目录的成员个数。通常传-1,表示读取目录所有文件对象。

    返回值:FileInfo类型的切片。其内部保存了文件名。error中保存错误信息。

// A FileInfo describes a file and is returned by Stat and Lstat.
type FileInfo interface {
    Name() string       // base name of the file
    Size() int64        // length in bytes for regular files; system-dependent for others
    Mode() FileMode     // file mode bits
    ModTime() time.Time // modification time
    IsDir() bool        // abbreviation for Mode().IsDir()
    Sys() interface{}   // underlying data source (can return nil)
}

得到FileInfo类型切片后,可以range遍历切片元素,使用.Name()获取文件名。使用.Size()获取文件大小,使用.IsDir()判断文件是目录还是非目录文件

如,我们可以提示用户提供一个目录位置,打开该目录,查看目录下的所有成员,并判别它们是文件还是目录。

package main

import (
    "fmt"
    "os"
)

func main() {

    // 获取用户输入的目录路径
    fmt.Println("请输入待查询的目录:")
    var path string
    path = "./"
    // 打开目录
    f, err := os.OpenFile(path, os.O_RDONLY, os.ModeDir)
    if err != nil {
        fmt.Println("Open dir error: ", err)
    }
    defer f.Close()

    // 读取目录项
    info, err := f.Readdir(-1)

    for _, fileInfo := range info{
        if fileInfo.IsDir(){
            fmt.Println(fileInfo.Name(), " 是一个目录")
        } else {
            fmt.Println(fileInfo.Name(), " 是一个文件c")
        }
    }

}
原文地址:https://www.cnblogs.com/aidata/p/11729979.html