青少年编程与数学 02_001 GO语言程序设计基础 02课题、项目构建
Go语言编程之项目构建,从基本的语法规则开始,先了解标识符、关键字、模块、包等基本概念,再通过具体开发环境中的项目创建与管理,开始你的Go语言学习之旅。为有利于初学,特别介绍了格式化输出输入的相关内容。
课题摘要
本课题主要内容包括:
- Go语言基本语法规则,包括标识符命名规则、变量声明、基本数据类型、控制结构、函数定义、包的概念、注释、作用域和可见性、初始化函数。
- 标识符的分类和使用,包括空白标识符、预声明标识符、唯一标识符和导出标识符。
- 关键字列表及其用途。
- 模块、包和文件的关系,项目结构的组织。
- 如何在VSCode和Goland中管理Go语言项目。
- 控制台的作用和如何在IDE中使用。
- 格式化输入与输出的函数和练习。
文档强调了Go语言的语法基础和编程规范,以及如何在IDE中高效地进行项目开发和管理。
一、Go语言的基本语法规则
Go语言的基本语法规则包括但不限于以下要点:
-
标识符:
- 标识符用于命名变量、常量、类型、函数、包等。
- 标识符由字母(a-z,A-Z)、数字(0-9)和下划线(_)组成。
- 标识符不能以数字开头。
- Go语言严格区分大小写,比如
myVariable
与MyVariable
是两个不同的标识符。 - 标识符中不能包含空格或其他特殊字符。
- 标识符不能使用Go语言的保留关键字作为名称。
-
变量声明:
- 使用
var
关键字可以显式声明变量,并可指定其数据类型。
var name string
- 简短声明(简写语法):在函数内部可以使用
:=
进行变量的声明和初始化,编译器会自动推导变量类型。
name := "Tom"
- 使用
-
基本数据类型:
- 整数类型:int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr
- 浮点数类型:float32, float64
- 布尔类型:bool
- 字符串类型:string
- 字符类型:rune (实际上是int32,代表一个Unicode码点)
- 字节类型:byte (实际上是uint8)
-
控制结构:
- 条件语句:if, else, switch
- 循环语句:for, while(通常通过for实现)
- 跳转语句:break, continue, goto, return
-
函数:
- 函数定义需要指定返回类型,如果没有返回值,则为
func function_name(参数列表) {}
。 - main函数是程序执行的入口点,必须在名为
main
的包内定义,且无参数无返回值。
- 函数定义需要指定返回类型,如果没有返回值,则为
-
包(Packages):
- 每个Go源文件属于一个包,通过
package
关键字指定包名。 import
关键字用来导入其他包,如标准库或自定义包。- 包级别的变量和函数可通过大写字母开头的标识符被外部包访问。
- 每个Go源文件属于一个包,通过
-
注释:
- 单行注释:
// 这是一个单行注释
- 多行注释:
/* 这是多行注释内容 */
- 单行注释:
-
作用域和可见性:
- 变量的作用域从声明处开始,直到所在块结束。
- 全局变量可以在整个包内可见,如果首字母大写,也可以在其他包中被引用。
-
初始化函数:
- 每个包都可以有一个或多个名为
init
的函数,它们会在包加载时自动调用,按照依赖关系排序执行。
- 每个包都可以有一个或多个名为
以上规则构成了Go语言基础语法的核心部分,但并非全部。实际编程中还会涉及接口、并发编程(goroutines和channels)、错误处理等诸多高级特性。
所涉及内容,在后面还要详细解析。
二、标识符
标识符在编程语言中,包括Go语言,是一个用于命名各种程序实体的名称。这些实体可能包括变量、常量、函数、类型、包、结构体、接口等。简单来说,标识符就是程序员为程序中的各个组成部分所起的名字。
在Go语言中,标识符的命名规范如下:
- 组成:标识符由字母(Unicode字母,可以包含非ASCII字符)、数字和下划线(_)组成。
- 首字符要求:标识符必须以一个字母或下划线开头,不能以数字开头。
- 大小写敏感:Go是大小写敏感的语言,因此
myVariable
与MyVariable
是两个不同的标识符。 - 保留字限制:标识符不能使用Go语言的保留关键字作为名称。
- 空标识符:下划线
_
也是一个有效的标识符,它通常用作占位符或者用于忽略函数返回值,但不应作为常规标识符使用。 - 驼峰命名法:Go语言推荐使用驼峰命名法(Camel Case),对于普通的局部变量和函数名,一般采用小驼峰式(lowerCamelCase),即首个单词首字母小写,后续每个单词首字母大写;而对于公开的(导出的)包成员(如变量、常量、类型、函数和方法),则建议使用大驼峰式(UpperCamelCase),即所有单词首字母均大写。
示例:
// 正确的标识符命名
var myVariable int
func myFunction() {}
type user struct{}
const maxItems uint = 100
// 公开(导出)的标识符应以大写字母开头
package somePackage
var PublicVar int
func PublicFunc() {}
type PublicType struct{}
此外,Go语言还强调代码的可读性和一致性,鼓励开发者使用有意义且描述性强的标识符名称来提高代码的可维护性。
三、标识符分类
(一)空白标识符(又称下划线 _
)
Go语言中的空白标识符(又称下划线 _
)是一个特殊的标识符,它具有多种用途和特性:
-
忽略值:
-
在函数返回多值时,如果不需要某个返回值,可以使用空白标识符来接收并丢弃这个值。
func multipleReturns() (int, error) { // ... return 42, nil } _, err := multipleReturns() // 这里只关心错误信息,因此用_来忽略第一个返回值
-
-
导入包而不使用其导出成员:
- 当你仅需要导入一个包以触发它的初始化代码执行(比如注册http处理程序),但并不直接使用该包的任何导出项时,可以使用空白标识符导入该包。
import _ "net/http/pprof"
- 当你仅需要导入一个包以触发它的初始化代码执行(比如注册http处理程序),但并不直接使用该包的任何导出项时,可以使用空白标识符导入该包。
-
循环中忽略元素:
- 在
for range
循环中,如果你只需要迭代中的一个值而不需要另一个值,可以将不关注的值赋给空白标识符。for _, c := range "hello" { fmt.Println(c) } // 这里仅打印字符串中的每个字符,不关心字符的索引位置
- 在
-
类型断言:
- 空白标识符可用于类型断言以检查接口类型的实现,但并不存储实际的值。
var reader io.Reader var _ io.Writer = reader // 如果reader不是io.Writer类型,这里会编译失败
- 空白标识符可用于类型断言以检查接口类型的实现,但并不存储实际的值。
-
匿名占位符:
- 空白标识符由于没有名称,常被看作是匿名占位符,用于在语法上满足某些需求,但实际上并不引用或存储任何数据。
总之,Go语言中的空白标识符是一种灵活的工具,主要用于在编程中表示对某个值或功能的有意忽视,或者用于特定的语义场景。
(二)预声明标识符
在Go语言中,预声明标识符是指那些在编译器内部已经定义和初始化的标识符,它们可以直接在任何包内使用,无需进行导入或其他显式声明。这些预声明标识符包括:
-
布尔型常量:
true
和false
-
空值:
nil
,它代表了接口、指针、映射、切片和通道类型的零值。
-
内置类型名称:
- 基本数字类型:
int
,int8
,int16
,int32
,int64
,uint
,uint8
(别名byte
),uint16
,uint32
,uint64
,uintptr
- 浮点数类型:
float32
,float64
- 复数类型:
complex64
,complex128
- 字符串类型:
string
- 编程原语:
rune
(用于表示单个Unicode码点,等同于int32
) - 时间相关类型:
time.Duration
- 基本数字类型:
-
内置函数:
- 数组、切片和映射操作:
append
,copy
,delete
,len
,cap
- 类型断言与转换:
new
,make
,typeAssert
- 数据类型判断与转换:
panic
,recover
,iota
,interface{}
(空接口) - 格式化与打印:
print
,println
,fmt.Printf
等一系列来自fmt
包的全局函数 - 内存分配与处理:
unsafe.Sizeof
,unsafe.Alignof
,unsafe.Offsetof
,unsafe.Pointer
- 数组、切片和映射操作:
-
特殊常量:
iota
是一个特殊的常量生成器,用在常量声明时自增计数。
需要注意的是,尽管有些预声明的标识符是函数或者具有类似函数的行为,但它们并非传统意义上的关键字,因为它们是可以被重新声明或赋值的。然而,这通常不推荐这样做,以免混淆代码逻辑并违反最佳实践。预声明标识符主要是为了提供方便的基础功能,使得开发者可以快速且高效地编写Go程序。
(三)唯一标识符
在Go语言中,标识符(identifiers)用于命名程序的各个组成部分,如变量、常量、函数、类型等。标识符的唯一性是基于其作用域和名称来实现的:
-
名称唯一性:
在同一作用域内,每个标识符必须具有唯一的名称。这意味着在同一文件、函数或块内部,你不能重复声明同名的变量、函数或其他标识符。例如,在同一个包里,你不能有两个同名的函数或类型。// 错误示例:在同一作用域内不能有相同名称的函数 func myFunction() { // ... } func myFunction() { // 编译错误:重复定义 // ... }
-
大小写敏感:
Go语言是大小写敏感的,因此MyVar
与myvar
被认为是两个不同的标识符。 -
导出规则与唯一性:
对于公开(exported)标识符,即首字母大写的标识符,它们在整个包外部也是唯一的。也就是说,不同包内的两个公开标识符可以有相同的名称,但当它们被导入到另一个包时,通过包名进行限定以确保唯一性。package pkgA type MyType struct{} // 在另一个包pkgB中 package pkgB import ( "pkgA" ) // 这里使用了完全限定的名字来引用pkgA中的MyType var a pkgA.MyType
-
作用域内的唯一性:
即使两个不同的包内存在相同名称的非公开标识符,只要它们在各自的作用域内不冲突,也不会产生问题,因为它们无法相互访问。 -
嵌套作用域:
在嵌套的作用域中,内部作用域可以重用外层作用域已使用的标识符,但这仅限于内部作用域,并不会影响外层作用域中该标识符的原有含义。
综上所述,在Go语言中,标识符的唯一性是由其名称、作用域以及大小写敏感性共同保证的。在合适的范围内,标识符必须保持唯一,而跨越不同范围时,可以通过包名等机制区分。
(四)导出标识符
在Go语言中,导出标识符是指那些可以被其他包访问的变量、常量、类型、函数和方法。Go语言通过标识符的首字母大小写来控制其可见性:
-
导出规则:
- 如果一个标识符(包括但不限于变量名、函数名、类型名等)的第一个字符是大写字母(Unicode类别“Lu”),那么这个标识符就是导出(exported)的。
- 如果一个标识符的第一个字符是小写字母或下划线,则它不是导出的,只能在定义它的同一个包内使用。
-
示例:
package mypackage // 导出的变量 var ExportedVar = "This is exported" // 非导出的变量 var unexportedVar = "This is not exported" // 导出的函数 func ExportedFunc() { // ... } // 非导出的函数 func unexportedFunc() { // ... } // 导出的类型 type ExportedType struct { Field string } // 导出的方法 func (e ExportedType) ExportedMethod() {} // 非导出的方法 func (e ExportedType) unexportedMethod() {}
-
外部包访问:
其他包想要访问mypackage
中的导出标识符时,需要通过导入声明,并使用包名加上标识符的方式来引用:package main import ( "mypackage" ) func main() { // 访问导出的变量 fmt.Println(mypackage.ExportedVar) // 调用导出的函数 mypackage.ExportedFunc() // 创建并使用导出的类型 e := mypackage.ExportedType{Field: "Value"} e.ExportedMethod() }
总结来说,在Go语言中,如果你想让某个标识符能够被其他包所看见和使用,那么必须确保该标识符以大写字母开头。这是Go语言实现封装和模块化设计的重要手段之一。
四、关键字
关键字在编程语言中是具有特殊含义的预定义标识符,它们不能用作变量名、函数名或其他用户自定义的标识符。关键字是由编程语言设计者保留的,用于实现特定语义或结构的关键字词组。
Go语言中的关键字如下:
break
:用于退出循环(如for或switch)。default
:在switch语句中表示默认分支。func
:用于声明函数和方法。interface
:用于定义接口类型。select
:用于并发编程中的通信控制,与channel操作相关联,类似于一个基于channel的多路复用选择器。case
:在switch或select语句中定义条件分支。chan
:用于创建通道类型,支持Go的并发特性。defer
:用于延迟执行函数调用,直到包含它的函数返回时才执行。struct
:定义结构体类型。goto
:跳转到标签(label)处。package
:定义代码包。switch
:用于条件多路选择。const
:声明常量。fallthrough
:在switch语句中使用,使得匹配的case之后不再检查其他条件,直接执行下一个case的语句块。map
:声明映射类型。if
和else
:用于条件判断。range
:用于迭代数组、切片、字符串、映射以及channel中的元素。type
:声明新类型或别名类型。continue
:在循环中跳过当前迭代的剩余部分,进入下一轮迭代。for
:通用循环语句,可以配合初始化、条件和后置表达式使用。import
:导入其他包。return
:从函数或方法返回值。var
:声明变量。go
:启动一个新的并发goroutine来执行函数。goto
:虽然存在,但在Go语言中不推荐使用,用于无条件跳转到同文件内的标记位置。
这些关键字对于理解和编写Go程序至关重要,它们构建了Go语言的语法基础,并且赋予了语言独特的特性和功能。
标识符和关键字是构成编程语言的基础。后面将逐渐展开。
五、模块(Module)、包(Package)和文件
Go 语言中的模块(Module)、包(Package)和文件有着紧密的关系,它们共同构成了 Go 语言的代码组织结构和依赖管理机制:
-
模块(Module):
- Go 语言从 1.11 版本开始引入了模块系统,用于管理和解决依赖问题。
- 模块是一个独立版本控制的单元,它包含一个或多个相关的 Go 包,并通过
go.mod
文件来声明模块的路径和版本信息。 - 每个模块都有一个唯一的模块路径,这通常是其源代码仓库的 URL 或者相对路径。
- 模块可以发布到公共仓库如 GitHub、GitLab 等,供其他开发者引用和下载。
-
包(Package):
- 包是 Go 语言中代码组织的基本单位,是相关功能的集合。
- 在文件系统上,每个包对应一个目录,该目录下的所有
.go
源文件都属于同一包。 - 每个 Go 源文件以
package
关键字开头声明自己所属的包名。 - 包内定义的函数、类型、变量和常量对同一包内的其他源文件可见;对外则通过导出(首字母大写)的接口与外部包进行交互。
- 包支持代码重用和封装,使得大型项目能够被划分为更易于理解和维护的小型组件。
-
文件(Files):
- Go 语言中的文件主要包括:
- 源代码文件:通常扩展名为
.go
,包含了实现特定功能的程序代码,这些文件归属于某个包。 - go.mod 文件:在模块根目录下,用于记录模块的依赖及其版本信息。
- go.sum 文件:存储了模块依赖项的预期哈希值,用于校验下载的依赖包是否完整和未被篡改。
- 执行文件:编译后的二进制文件,可以直接运行。
- 库文件(也即归档文件 .a):编译后生成的包对象文件,存放在
$GOPATH/pkg
或模块缓存中,作为构建其他软件时的依赖。
- 源代码文件:通常扩展名为
- Go 语言中的文件主要包括:
六、项目结构
Go项目可以包含多个模块,每个模块都是一个独立的代码组织和依赖管理单元。从Go 1.18版本开始,通过引入多模块工作区(Workspaces)特性,开发者可以在一个工作区中同时处理多个模块。
在单个仓库中发布多个模块的方法:
- 目录结构:
- 在项目根目录下,为每个模块创建不同的子目录。
- 每个子目录代表一个单独的Go模块,并且需要在其内部初始化一个新的
go.mod
文件。
例如:
my-project/
├── module1/
│ └── go.mod
│ └── ...
├── module2/
│ └── go.mod
│ └── ...
└── README.md
-
模块初始化:
- 在每个模块的子目录下运行
go mod init <module-name>
来初始化模块,其中<module-name>
是该模块对应的路径名。
- 在每个模块的子目录下运行
-
多模块工作区(Go 1.18+):
- 使用
go work use
命令可以将多个模块添加到同一个工作区:cd my-project go work init go work use ./module1 ./module2
- 此时,所有指定的模块都会被加入到
.work
文件中,开发工具会识别这个工作区并支持跨模块操作。
- 使用
-
开发与构建:
- 在这样的多模块项目中,你可以分别在各个模块内进行开发、构建和测试。
- 如果需要跨模块编译或执行,确保按照模块间引用关系正确处理。
-
版本控制与发布:
- 即使在一个仓库中,每个模块仍然可以拥有自己的版本控制系统分支和标签。
- 发布时,可以通过Git Tag等方式标记每个模块的特定版本,并在其他项目中通过相应的模块路径和版本号来引用它们。
-
外部项目的使用:
- 其他项目可以通过各自模块的路径和版本来导入这些模块。
总之,在Go语言中,虽然一个仓库可以包含多个模块,但每个模块应该保持其逻辑上的独立性,并且通过各自的 go.mod
文件来管理和声明依赖。Go 1.18及更高版本的工作区特性极大地简化了在同一仓库中对多个模块的操作和管理。
七、VsCode项目管理
在Visual Studio Code(VSCode)中组织和管理一个Go语言项目,可以按照以下步骤进行:
-
安装Go插件:
- 打开VSCode。
- 点击左侧活动栏的“Extensions”图标(或使用快捷键
Ctrl + Shift + X
)。 - 在搜索框中输入“Go”,找到并安装官方的“Go for Visual Studio Code”插件。
-
设置Go环境:
- 确保已经在系统上正确安装了Go,并设置了GOPATH环境变量。对于Go 1.11及以上版本,推荐使用Go Modules功能来管理依赖,这样就不需要显式设置GOPATH。
- 如果使用Go Modules,确保在终端运行
go env -w GO111MODULE=on
以启用模块模式。
-
创建项目结构:
- 在文件系统中创建一个新的目录作为项目根目录。
- 在项目根目录下创建
src
子目录(如果是使用Go Modules则直接在根目录下放置源代码)。 - 根据包结构创建相应的子目录,每个子目录代表一个Go包,其中包含
.go
源代码文件,并且每个文件开头声明正确的package
名称。
-
初始化Go Modules(如果适用):
- 在项目根目录打开终端(通过VSCode内置的终端或外部终端)。
- 运行
go mod init <module-name>
来初始化一个新的模块,<module-name>
是你的模块路径。
-
依赖管理:
- 使用命令
go get <dependency>
添加依赖项,或者在编辑器内直接使用插件提供的功能添加、更新或移除依赖。
- 使用命令
-
配置VSCode:
- 打开VSCode的工作区设置(
File > Preferences > Settings
或Ctrl + ,
),确保Go插件已正确识别Go SDK的位置。 - 可以根据需要自定义Go相关设置,例如:格式化工具、代码片段、自动导入包等。
- 打开VSCode的工作区设置(
-
编写代码与构建:
- 直接在VSCode中编写Go源码,插件会提供语法高亮、代码补全、错误检查等功能。
- 可以通过集成终端或任务运行
go build
、go run
和go test
等命令来编译和运行项目。
-
调试项目:
- 创建一个调试配置文件
.vscode/launch.json
,用于定义如何启动和调试程序。 - 插件提供了模板帮助创建调试配置,包括针对本地应用、远程应用和测试用例的调试场景。
- 创建一个调试配置文件
-
代码导航与重构:
- 利用VSCode的跳转到定义、查找引用、重命名符号等功能来管理和重构代码。
通过上述步骤,您可以在VSCode中有效地组织和管理Go语言项目。请注意,具体的步骤可能会随着时间和VSCode Go插件版本的更新而有所变化。
八、Goland项目管理
在Goland中组织和管理一个Go项目,可以按照以下步骤进行:
-
创建新项目:
- 打开Goland IDE。
- 选择“File”菜单,然后点击“New Project”(或从欢迎界面直接点击“Create New Project”)。
- 在新建项目的向导中,选择“Go”作为项目类型,并确保Go的SDK已正确配置。
- 设置项目名称、位置以及项目结构。可以选择使用 Go Modules 进行依赖管理,这将在项目根目录下自动生成
go.mod
文件。
-
模块初始化:
- 如果是Go Modules项目,在终端窗口(可以通过IDE内集成的终端或者快捷键打开)进入项目根目录并运行命令
go mod init <module-name>
初始化一个新的模块,其中<module-name>
是你的模块路径。
- 如果是Go Modules项目,在终端窗口(可以通过IDE内集成的终端或者快捷键打开)进入项目根目录并运行命令
-
依赖管理:
- 添加依赖时,在终端中执行
go get <dependency>
来下载和安装所需包。 - Goland还提供了图形化的方式来添加和管理依赖,可以在“Preferences” -> “Go” -> “Go Modules(vgo)”下查看和编辑依赖关系。
- 添加依赖时,在终端中执行
-
文件与包组织:
- 在项目中,按照Go语言的包结构组织文件夹和源代码文件。每个包对应一个目录,目录下的所有
.go
文件应具有相同的package
声明。 - 使用Goland的文件浏览器来创建、移动、删除文件和目录,以保持项目的整洁和逻辑清晰。
- 在项目中,按照Go语言的包结构组织文件夹和源代码文件。每个包对应一个目录,目录下的所有
-
工作区设置:
- 若需同时管理多个项目,可将它们置于同一个工作区(Workspace)。在Goland中,可以通过“File”->“Open”或“Welcome”界面中的“Open”选项来打开一个包含多个项目的目录作为工作区。
-
版本控制:
- Goland集成了版本控制系统如Git,可以直接在IDE内部进行Git操作,如:初始化仓库 (
git init
)、添加文件到暂存区 (git add
)、提交 (git commit
)、拉取/推送 (git pull/push
) 和克隆远程仓库 (git clone
) 等。
- Goland集成了版本控制系统如Git,可以直接在IDE内部进行Git操作,如:初始化仓库 (
-
构建与运行:
- 配置运行配置以启动项目,可以为不同的主程序或测试编写多个运行配置。
- 可以通过右击文件或函数名,选择“Run ‘main.go’”(或其他指定的入口文件)来运行项目。
- 对于服务类应用,还可以设置调试会话和热重载功能。
-
代码导航与重构:
- 利用Goland强大的代码补全、跳转到定义、重构工具等特性来高效地开发和维护项目。
-
调试与测试:
- 设置断点,通过Goland的内置调试器来调试应用程序。
- 创建和运行测试用例,查看测试结果及覆盖率报告。
以上是一个基本的概述,实际操作过程中,Goland提供了一系列高级功能和自动化工具来简化这些步骤,帮助开发者更高效地管理和开发Go语言项目。
有关项目组织与管理在学习过程将不断深入。这里列出的内容可供以后查询。
九、控制台
在软件开发工具(如Goland)中,控制台通常指的是集成开发环境(IDE)中的一个组件或面板,它模拟了命令行界面(CLI),允许开发者直接在IDE内部运行和调试程序,以及与操作系统进行交互。这个“控制台”提供了对系统命令行的访问,无需离开IDE即可执行命令、运行脚本、编译代码、查看输出、启动服务器进程等操作。
具体到Goland这样的Java/Go语言IDE中,控制台可能有如下用途:
- 运行应用程序:可以直接在控制台内运行程序并观察其标准输出(stdout)和标准错误输出(stderr)。
- 编译构建:通过命令行调用编译器,如
go build
命令来编译Go语言项目,或者使用Maven、Gradle等构建工具。 - 测试执行:运行自动化测试,并显示测试结果。
- 调试支持:提供调试控制台,可以在此设置断点、单步执行、查看变量值等。
- 版本控制操作:虽然不是必须,但一些IDE的控制台可能会集成Git或其他版本控制系统命令,使得用户能够在控制台窗口内执行版本控制相关的操作。
- 数据库操作:某些IDE的控制台还可以用于执行SQL查询和其他数据库管理任务。
总之,在软件开发过程中,IDE内的控制台极大地方便了开发人员快速执行和监控各种命令行任务,增强了工作效率,减少了切换不同窗口和工具的需要。
控制台输入与输出在实际开发中较少用到。但在初阶段可以用来测试所编写的代码产生的结果。此外,格式化输出在实际编程中要经常用到,本节内容应多加练习。
十、格式化输入与输出
(一)输出函数
Go 语言中的 fmt
包提供了格式化输入(扫描,如 Scan
, Scanf
, Scanln
)和输出(打印,如 Print
, Printf
, Println
, Sprintf
, Fprint
, 等)的功能。这里主要讲解其输出功能的详细应用示例:
-
基本输出函数:
-
fmt.Print
: 打印所有参数,并在它们之间自动添加空格。package main import "fmt" func main() { fmt.Print("Hello, ", "World!") }
-
fmt.Println
: 类似于Print
,但在所有参数之后会追加一个换行符。fmt.Println("Hello, World!")
-
-
格式化输出函数:
-
fmt.Printf
: 格式化并打印变量,接受一个格式字符串和任意数量的参数。fmt.Printf("My name is %s and I am %d years old.\n", "Alice", 30)
这里
%s
是用于字符串的占位符,%d
是用于整数的占位符。
-
-
更复杂的格式化选项:
-
使用标志(flag)来改变输出样式,例如左对齐
-
、填充0
、宽度.width
等。fmt.Printf("|%6s|%6d|\n", "Name", 123) // 默认右对齐,总宽度为6 fmt.Printf("|%-6s|%-6d|\n", "Name", 123) // 左对齐 fmt.Printf("|%06s|%06d|\n", "Name", 123) // 用0填充空白
-
输出类型标识符:
%v
:值的默认表示形式(根据类型不同而变化)%#v
:值的 Go 语法表示%T
:值的类型的 Go 语法表示%t
:布尔型%d
,%i
:十进制整数%x
,%X
:十六进制整数%e
,%E
:科学记数法浮点数%f
:浮点数%g
或%G
:根据情况选择%e
或%f
%%
:输出百分号%
自身
-
-
其他输出函数:
-
fmt.Sprintf
:将格式化的字符串写入到新的字符串变量中,而不是直接输出到控制台。greeting := fmt.Sprintf("Hello, %s!", "Bob") fmt.Println(greeting) // 输出:Hello, Bob!
-
fmt.Fprint
和相关函数:这些函数可以将格式化内容写入到任意实现了 io.Writer 接口的对象,比如 os.File 或 bufio.Writer。file, _ := os.Create("output.txt") defer file.Close() fmt.Fprintf(file, "Writing to a file: %s\n", "This is a test.")
-
以上仅为部分示例,fmt
包提供的功能非常丰富,实际使用时可以根据需求进行组合和扩展。
(二)格式化输出练习一
以下是一个综合性的示例,展示了如何使用 fmt
包进行格式化输出,包括不同类型的数据和更复杂的格式控制:
package main
import (
"fmt"
"time"
)
type Person struct {
Name string
Age int
}
func main() {
// 基本类型输出
fmt.Println("Simple output:")
fmt.Println("Hello, World!")
fmt.Println(123)
fmt.Println(123.456)
// 格式化输出
fmt.Println("\nFormatted output:")
person := Person{Name: "Alice", Age: 30}
now := time.Now()
// 使用占位符 %s, %d 和 %.2f 分别对应字符串、整数和保留两位小数的浮点数
fmt.Printf("Person: Name = %s, Age = %d\n", person.Name, person.Age)
fmt.Printf("Current time: %v\n", now) // 对于time.Time类型,%v会输出可读的时间格式
// 使用宽度和填充
fmt.Printf("|%-10s|%-10d|\n", "Name", 123) // 左对齐,总宽度为10
fmt.Printf("|%10s|%10d|\n", "Name", 123) // 右对齐,总宽度为10,用空格填充
fmt.Printf("|%010s|%010d|\n", "Name", 123) // 右对齐,总宽度为10,用0填充
// 使用 %+v 输出结构体时会包含字段名
fmt.Printf("\nThe Person struct using %+v:\n%+v\n", person, person)
// 复杂的格式化示例,输出布尔值、指针以及 Go 语法表示的类型和值
boolValue := true
intPtr := new(int)
*intPtr = 42
fmt.Printf("\nOther types and flags:\n%t, %#v, %p, %T\n", boolValue, intPtr, intPtr, intPtr)
}
运行此程序将看到各种类型的变量以不同方式格式化输出的结果。
(三)格式化输出
1. fmt.Print系列函数
fmt.Println
: 输出内容并自动添加换行符。fmt.Printf
: 格式化的输出,允许按照指定的格式字符串打印变量值。fmt.Print
: 输出内容,不添加换行符。
package main
import "fmt"
func main() {
fmt.Println("Hello, World!") // 自动添加换行
fmt.Printf("Today is %s\n", "2024年2月21日") // 格式化输出日期
fmt.Print("No newline here") // 不会自动添加换行
}
2. Printf格式说明
Printf
的格式字符串中可以包含特殊的占位符(如 %d
, %s
, %v
, %T
等),它们会被相应类型的变量替换:
%d
: 整数%f
或%g
: 浮点数或科学计数法表示的数字%t
: 布尔类型%s
: 字符串%v
: 值的默认格式表示(根据不同类型显示)%#v
: 类似%v
,但输出结构体时会包含字段名%T
: 输出值的类型名称%q
: 双引号包裹的字符串字面量形式(适用于字符串和字符)%p
: 指针的内存地址
示例:
fmt.Printf("%d, %.2f, %t, %s, %#v, %T\n", 123, 3.14159, true, "Golang", struct{A int}{A: 42}, myVar)
3. 格式化布尔类型
布尔类型的值在使用 %t
格式化时,会输出 true
或 false
。
var isDone bool = false
fmt.Printf("Task is done? %t\n", isDone) // 输出:Task is done? false
(四)格式化输入
1. fmt.Scan系列函数
fmt.Scan
: 从标准输入读取数据到一系列变量中,空格作为分隔符。fmt.Scanf
: 类似于 C 中的scanf
,接受一个格式字符串和对应变量列表,根据格式进行输入。fmt.Scanln
: 类似于Scanf
,但在读取一行后停止,并丢弃结尾的换行符。fmt.Sscan
: 从字符串中扫描。fmt.Sscanf
: 类似于Scanf
,但是针对字符串而不是标准输入。fmt.Sscanln
: 对字符串进行类似 Scanln 的操作。
示例:
var name string
var age int
fmt.Println("请输入姓名和年龄(用空格分隔)")
_, _ = fmt.Scan(&name, &age) // 从键盘输入读取姓名和年龄
// 或者使用 Sscanf 从已知字符串读取
input := "Alice 30"
fmt.Sscanf(input, "%s %d", &name, &age)
注意事项
- 使用格式化输入时务必注意安全性和有效性检查,确保输入的数据能够正确转换为预期类型。
- 在处理用户输入时,应尽可能避免直接将输入用于数据库查询等敏感操作,防止 SQL 注入等问题。
- Go 语言推荐使用
fmt.Scan
和fmt.Scanln
的变体fmt.Scanf
和fmt.Scanf
以及其配套的格式字符串进行精确控制输入解析。
(五)格式化输出练习二
type user struct {
name string
}
func main() {
u := user{"guo"}
//Printf 格式化输出
fmt.Printf("% + v\n", u) //格式化输出结构
fmt.Printf("%#v\n", u) //输出值的 Go 语言表示方法
fmt.Printf("%T\n", u) //输出值的类型的 Go 语言表示
fmt.Printf("%t\n", true) //输出值的 true 或 false
fmt.Printf("%b\n", 1024) //二进制表示
fmt.Printf("%c\n", 11111111) //数值对应的 Unicode 编码字符
fmt.Printf("%d\n", 10) //十进制表示
fmt.Printf("%o\n", 8) //八进制表示
fmt.Printf("%q\n", 22) //转化为十六进制并附上单引号
fmt.Printf("%x\n", 1223) //十六进制表示,用a-f表示
fmt.Printf("%X\n", 1223) //十六进制表示,用A-F表示
fmt.Printf("%U\n", 1233) //Unicode表示
fmt.Printf("%b\n", 12.34) //无小数部分,两位指数的科学计数法6946802425218990p-49
fmt.Printf("%e\n", 12.345) //科学计数法,e表示
fmt.Printf("%E\n", 12.34455) //科学计数法,E表示
fmt.Printf("%f\n", 12.3456) //有小数部分,无指数部分
fmt.Printf("%g\n", 12.3456) //根据实际情况采用%e或%f输出
fmt.Printf("%G\n", 12.3456) //根据实际情况采用%E或%f输出
fmt.Printf("%s\n", "wqdew") //直接输出字符串或者[]byte
fmt.Printf("%q\n", "dedede") //双引号括起来的字符串
fmt.Printf("%x\n", "abczxc") //每个字节用两字节十六进制表示,a-f表示
fmt.Printf("%X\n", "asdzxc") //每个字节用两字节十六进制表示,A-F表示
fmt.Printf("%p\n", 0x123) //0x开头的十六进制数表示
}
初学必备知识。实际开发中也会经常用到。