超赞的 Go 语言 INI 文件操作

前言

INI 是 Windows 上常用的配置文件格式。如果你使用 INI 作为系统的配置文件,那么一定会使用这个库吧。它就是号称地表 最强大、最方便 和 最流行 的 Go 语言 INI 文件操作库。配置文件形式为[section] 的段构成, 内部使用 name=value键值对 。go-ini是 Go 语言中用于操作 ini 文件的第三方库。本文介绍go-ini库的使用。

INI官方

该项目的作者也是很多 Go 语言爱好者熟悉的无闻大师。讲真,文档都写的很好,很用心。

官方网站:https://ini.unknwon.io/

在这里插入图片描述

功能特性

支持覆盖加载多个数据源([]byte、文件和 io.ReadCloser)
支持递归读取键值
支持读取父子分区
支持读取自增键名
支持读取多行的键值
支持大量辅助方法
支持在读取时直接转换为 Go 语言类型
支持读取和 写入 分区和键的注释
轻松操作分区、键值和注释
在保存文件时分区和键值会保持原有的顺序

安装下载

go-ini 是第三方库,使用前需要安装,最低要求安装 Go 语言版本为 1.6。安装时切换到root用户,否则会报错(permission denied)。

安装:

go get gopkg.in/ini.v1

则gopkg.in会出现在src目录下:
在这里插入图片描述
也可以使用 GitHub 上的仓库:

go get github.com/go-ini/ini

开始使用

我们将通过一个非常简单的例子来了解如何使用。首先,我们需要在src目录下,任意创建两个文件(my.ini 和 main.go)。

首先,创建一个my.ini配置文件:

app_name = awesome web

# possible values: DEBUG, INFO, WARNING, ERROR, FATAL
log_level = DEBUG

[mysql]
ip = 127.0.0.1
port = 3306
user = minger
password = 123456
database = awesome

[redis]
ip = 127.0.0.1
port = 6379

在 ini 文件中,每个键值对占用一行,中间使用=隔开。以#开头的内容为注释。ini 文件是以分区(section)组织的。在下一个分区前结束。所有分区前的内容属于默认分区,如my.ini文件中的app_name和log_level。

接下来我们需要编写 main.go 文件来操作刚才创建的my.ini配置文件。

package main

import (
  "fmt"
  "log"

  "gopkg.in/ini.v1"
)

func main() {
  cfg, err := ini.Load("my.ini")
  if err != nil {
    log.Fatal("Fail to read file: ", err)
  }

  // 典型读取操作,默认分区可以使用空字符串表示
  fmt.Println("App Name:", cfg.Section("").Key("app_name").String())
  fmt.Println("Log Level:", cfg.Section("").Key("log_level").String())

   //获取一个类型为字符串(string)的值
  fmt.Println("MySQL IP:", cfg.Section("mysql").Key("ip").String())
   //获取一个类型为整形(int)的值
  mysqlPort, err := cfg.Section("mysql").Key("port").Int()
  if err != nil {
    log.Fatal(err)
  }
  fmt.Println("MySQL Port:", mysqlPort)
  fmt.Println("MySQL User:", cfg.Section("mysql").Key("user").String())
  fmt.Println("MySQL Password:", cfg.Section("mysql").Key("password").String())
  fmt.Println("MySQL Database:", cfg.Section("mysql").Key("database").String())

  //go-ini也提供对应的MustType(Type 为Init/Uint/Float64等)方法,这个方法只返回一个值。
  //fmt.Println("redis Port:", cfg.Section("redis").Key("port").MustInt(6381))

  //获取一个类型为字符串(string)的值
  fmt.Println("Redis IP:", cfg.Section("redis").Key("ip").String())
  redisPort, err := cfg.Section("redis").Key("port").Int()
  if err != nil {
    log.Fatal(err)
  }
  fmt.Println("Redis Port:", redisPort)
}

编译输出:

在这里插入图片描述
从上面的信息,配置文件中存储的都是字符串,所以类型为字符串的配置项不会出现类型转换失败的,故String()方法只返回一个值。
但如果类型为Int/Uint/Float64这些时,转换可能失败。

使用go-ini读取配置文件的步骤如下:


1、首先调用ini.Load加载文件,得到配置对象cfg;

2、然后以分区名调用配置对象的Section方法得到对应的分区对象section,默认分区的名字为"",也可以使用ini.DefaultSection;

3、以键名调用分区对象的Key方法得到对应的配置项key对象;

4、由于文件中读取出来的都是字符串,key对象需根据类型调用对应的方法返回具体类型的值使用,如上面的String、MustInt方法。

分区操作

在加载配置之后,可以通过Sections方法获取所有分区,SectionStrings()方法获取所有分区名。例如:

package main

import (
  "fmt"
  "os"

  "gopkg.in/ini.v1"
)

func main() {

    cfg, err := ini.Load("my.ini")
    if err != nil {
        fmt.Printf("Fail to read file: %v", err)
        os.Exit(1)
    }

    sections := cfg.Sections()
    names := cfg.SectionStrings()
    
    fmt.Println("sections: ", sections)
    fmt.Println("names: ", names)
}

编译输出:

在这里插入图片描述
调用Section(name)获取名为name的分区,如果该分区不存在,则自动创建一个分区返回,例子如下:

package main

import (
  "fmt"
  "os"

  "gopkg.in/ini.v1"
)

func main() {

    cfg, err := ini.Load("my.ini")
    if err != nil {
        fmt.Printf("Fail to read file: %v", err)
        os.Exit(1)
    }

    newSection := cfg.Section("new")

    fmt.Println("new section: ", newSection)
    fmt.Println("names: ", cfg.SectionStrings())
}

编译输出:

在这里插入图片描述

保存配置

有时候,我们需要将生成的配置写到文件中。例如在写工具的时候。保存有两种类型的接口,一种直接保存到文件,另一种写入到io.Writer中:

下面我们通过程序生成前面使用的配置文件my.ini并保存:

package main

import (
  "fmt"
  "os"

  "gopkg.in/ini.v1"
)

func main() {
  cfg := ini.Empty()

  defaultSection := cfg.Section("")
  defaultSection.NewKey("app_name", "awesome web")
  defaultSection.NewKey("log_level", "DEBUG")

  mysqlSection, err := cfg.NewSection("mysql")
  if err != nil {
    fmt.Println("new mysql section failed:", err)
    return
  }
  mysqlSection.NewKey("ip", "127.0.0.1")
  mysqlSection.NewKey("port", "3306")
  mysqlSection.NewKey("user", "root")
  mysqlSection.NewKey("password", "123456")
  mysqlSection.NewKey("database", "awesome")

  redisSection, err := cfg.NewSection("redis")
  if err != nil {
    fmt.Println("new redis section failed:", err)
    return
  }
  redisSection.NewKey("ip", "127.0.0.1")
  redisSection.NewKey("port", "6379")

  err = cfg.SaveTo("my.ini")
  if err != nil {
    fmt.Println("SaveTo failed: ", err)
  }

  err = cfg.SaveToIndent("my-pretty.ini", "\t") //保存文件为 my-pretty.ini
  if err != nil {
    fmt.Println("SaveToIndent failed: ", err)
  }

  cfg.WriteTo(os.Stdout)
  fmt.Println()
//   cfg.WriteToIndent(os.Stdout, "\t")
}

编译输出:

在这里插入图片描述
my-pretty.ini

在这里插入图片描述
下面来看看分区与结构体字段映射的使用。

分区与结构体字段映射

我们将通过一个非常简单的例子来了解分区与结构体字段映射如何使用。例如:

package main

import (
  "fmt"

  "gopkg.in/ini.v1"
)

type Config struct {
  AppName   string `ini:"app_name"`
  LogLevel  string `ini:"log_level"`

  MySQL     MySQLConfig `ini:"mysql"`
  Redis     RedisConfig `ini:"redis"`
}

type MySQLConfig struct {
  IP        string `ini:"ip"`
  Port      int `ini:"port"`
  User      string `ini:"user"`
  Password  string `ini:"password"`
  Database  string `ini:"database"`
}

type RedisConfig struct {
  IP      string `ini:"ip"`
  Port    int `ini:"port"`
}

func main() {
  cfg, err := ini.Load("my.ini")
  if err != nil {
    fmt.Println("load my.ini failed: ", err)
  }

  c := Config{}
  cfg.MapTo(&c)

  fmt.Println(c)
}

编译输出:

在这里插入图片描述
MapTo内部使用了反射,所以结构体字段必须都是导出的。如果键名与字段名不相同,那么需要在结构标签中指定对应的键名。

结构体生成配置

package main

import (
  "fmt"
  "os"

  "gopkg.in/ini.v1"
)

type Config struct {
  AppName   string `ini:"app_name"`
  LogLevel  string `ini:"log_level"`

  MySQL     MySQLConfig `ini:"mysql"`
  Redis     RedisConfig `ini:"redis"`
}

type MySQLConfig struct {
  IP        string `ini:"ip"`
  Port      int `ini:"port"`
  User      string `ini:"user"`
  Password  string `ini:"password"`
  Database  string `ini:"database"`
}

type RedisConfig struct {
  IP      string `ini:"ip"`
  Port    int `ini:"port"`
}

func main() {
  cfg, err := ini.Load("my.ini")
  if err != nil {
    fmt.Println("load my.ini failed: ", err)
  }

  cfg = ini.Empty()

  c := Config {
    AppName:     "awesome web",
    LogLevel:     "DEBUG",
    MySQL: MySQLConfig {
      IP:     "127.0.0.1",
      Port:    3306,
      User:    "root",
      Password:"123456",
      Database:"awesome",
    },
    Redis: RedisConfig {
      IP:        "127.0.0.1",
      Port:    6379,
    },
  }
  
  err = ini.ReflectFrom(cfg, &c)
  if err != nil {
    fmt.Println("ReflectFrom failed: ", err)
    return
  }
  
  err = cfg.SaveTo("my-copy.ini")
  if err != nil {
    fmt.Println("SaveTo failed: ", err)
    return
  }

  cfg.WriteTo(os.Stdout)
  fmt.Println()

}

编译输出:
在这里插入图片描述

总结

go-ini 是 Go 语言中用于操作 ini 文件的第三方库。go-ini还有很多高级特性。官方文档非常详细,推荐去看一看。

参考:go-ini 官方文档
go-ini GitHub 仓库
https://studygolang.com/articles/26135

在这里插入图片描述

欢迎关注公众号【程序猿编码】,添加本人微信号(17865354792),回复:领取学习资料,网盘资料有如下:

在这里插入图片描述

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Pygame模块可以用来实现很多有趣的游戏,其中包括经典的贪吃蛇游戏。通过使用Pygame模块的绘图功能,可以实现一个具有视觉效果的贪吃蛇游戏。 下面是一个简单的贪吃蛇游戏的实现: ```python import pygame import random # 初始化游戏 pygame.init() # 定义游戏窗口大小 win_width = 800 win_height = 600 # 创建游戏窗口 win = pygame.display.set_mode((win_width, win_height)) # 设置游戏标题 pygame.display.set_caption("贪吃蛇") # 定义颜色 white = (255, 255, 255) black = (0, 0, 0) red = (255, 0, 0) green = (0, 255, 0) # 设置游戏时钟 clock = pygame.time.Clock() # 定义蛇的属性 snake_block = 10 snake_speed = 15 # 定义字体 font_style = pygame.font.SysFont(None, 50) # 定义函数,用于显示文字 def message(msg, color): message = font_style.render(msg, True, color) win.blit(message, [win_width / 6, win_height / 3]) # 定义函数,用于绘制蛇 def draw_snake(snake_block, snake_list): for x in snake_list: pygame.draw.rect(win, green, [x[0], x[1], snake_block, snake_block]) # 定义游戏循环 def game_loop(): game_over = False game_close = False # 定义蛇的初始位置 x1 = win_width / 2 y1 = win_height / 2 # 定义蛇的移动方向 x1_change = 0 y1_change = 0 # 定义蛇的初始长度 snake_List = [] Length_of_snake = 1 # 定义食物的初始位置 foodx = round(random.randrange(0, win_width - snake_block) / 10.0) * 10.0 foody = round(random.randrange(0, win_height - snake_block) / 10.0) * 10.0 # 游戏循环 while not game_over: while game_close == True: win.fill(white) message("你输了,按 Q 退出,按 C 重新开始!", red) pygame.display.update() for event in pygame.event.get(): if event.type == pygame.KEYDOWN: if event.key == pygame.K_q: game_over = True game_close = False if event.key == pygame.K_c: game_loop() for event in pygame.event.get(): if event.type == pygame.QUIT: game_over = True if event.type == pygame.KEYDOWN: if event.key == pygame.K_LEFT: x1_change = -snake_block y1_change = 0 elif event.key == pygame.K_RIGHT: x1_change = snake_block y1_change = 0 elif event.key == pygame.K_UP: y1_change = -snake_block x1_change = 0 elif event.key == pygame.K_DOWN: y1_change = snake_block x1_change = 0 # 判断蛇是否超出边界 if x1 >= win_width or x1 < 0 or y1 >= win_height or y1 < 0: game_close = True # 更新蛇的位置 x1 += x1_change y1 += y1_change # 绘制游戏背景 win.fill(white) # 绘制食物 pygame.draw.rect(win, red, [foodx, foody, snake_block, snake_block]) # 更新蛇的长度 snake_Head = [] snake_Head.append(x1) snake_Head.append(y1) snake_List.append(snake_Head) if len(snake_List) > Length_of_snake: del snake_List[0] # 判断蛇是否吃到食物 for x in snake_List[:-1]: if x == snake_Head: game_close = True # 绘制蛇 draw_snake(snake_block, snake_List) # 更新游戏窗口 pygame.display.update() # 判断蛇是否吃到食物 if x1 == foodx and y1 == foody: foodx = round(random.randrange(0, win_width - snake_block) / 10.0) * 10.0 foody = round(random.randrange(0, win_height - snake_block) / 10.0) * 10.0 Length_of_snake += 1 # 设置游戏速度 clock.tick(snake_speed) # 退出游戏 pygame.quit() quit() # 启动游戏循环 game_loop() ``` 这个程序可以实现一个基本的贪吃蛇游戏,玩家可以使用方向键控制蛇的移动方向,吃到食物后蛇的长度会增加。当蛇碰到边界或自己时,游戏结束。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值