当前位置: 首页 > news >正文

Go基础:输入与输出格式化详解

文章目录

    • 一、Go语言输入与输出概述
    • 二、输出格式化详解
      • 2.1 常用输出函数
      • 2.2 格式化动词(Verbs)
      • 2.3 案例
    • 三、输入格式化详解
      • 3.1 常用输入函数
      • 3.2 案例
    • 四、Go语言 `bufio` 详解
      • 4.1 为什么需要 `bufio`?(核心思想)
      • 4.2 `bufio.Reader`:带缓冲的读取器
      • 4.3 `bufio.Writer`:带缓冲的写入器
      • 4.4 `bufio.Scanner`:更高级的读取工具
      • 4.5 使用对比

一、Go语言输入与输出概述

Go语言中,输入与输出(I/O)操作主要通过 fmt 包和 bufio 包实现。fmt 包提供了基本的格式化输入输出功能,而 bufio 包则用于更高效的缓冲读写操作。

二、输出格式化详解

2.1 常用输出函数

Go语言中,fmt 包提供了以下常用输出函数:

  • fmt.Print(a ...interface{}):打印参数,不添加换行。
  • fmt.Println(a ...interface{}):打印参数,自动添加空格和换行。
  • fmt.Printf(format string, a ...interface{}):按格式化字符串打印。
  • fmt.Sprintf(format string, a ...interface{}):返回格式化后的字符串,不打印。
  • fmt.Fprintf(w io.Writer, format string, a ...interface{}):将格式化内容写入 io.Writer

2.2 格式化动词(Verbs)

格式化动词用于控制输出格式,常用动词包括:

动词说明
%v值的默认格式
%+v类似 %v,但输出结构体时会添加字段名
%#v值的 Go 语法表示
%T值的类型
%%百分号
%d十进制整数
%b二进制整数
%x十六进制整数(小写)
%f浮点数
%s字符串
%p指针地址

2.3 案例

package main
import ("fmt""os"
)
type Person struct {Name stringAge  int
}
func main() {// 基本输出fmt.Print("Hello, ")fmt.Println("World!")// 格式化输出name := "Alice"age := 25fmt.Printf("Name: %s, Age: %d\n", name, age)// 结构体格式化person := Person{Name: "Bob", Age: 30}fmt.Printf("Person: %v\n", person)         // 输出: Person: {Bob 30}fmt.Printf("Person: %+v\n", person)        // 输出: Person: {Name:Bob Age:30}fmt.Printf("Person: %#v\n", person)        // 输出: Person: main.Person{Name:"Bob", Age:30}// 返回格式化字符串formattedStr := fmt.Sprintf("Name: %s, Age: %d", name, age)fmt.Println("Formatted string:", formattedStr)// 写入文件file, err := os.Create("output.txt")if err != nil {fmt.Println("Error creating file:", err)return}defer file.Close()fmt.Fprintf(file, "This is written to a file. Name: %s, Age: %d\n", name, age)fmt.Println("Data written to file.")
}

三、输入格式化详解

3.1 常用输入函数

Go语言中,fmt 包提供了以下常用输入函数:

  • fmt.Scan(a ...interface{}):从标准输入读取数据,按空格分隔。
  • fmt.Scanln(a ...interface{}):类似 Scan,但读取到换行符时停止。
  • fmt.Scanf(format string, a ...interface{}):按格式化字符串读取输入。

3.2 案例

package main
import ("bufio""fmt""os""strings"
)
func main() {// 使用 fmt.Scan 读取输入var name stringvar age intfmt.Print("Enter your name and age (separated by space): ")fmt.Scan(&name, &age)fmt.Printf("Name: %s, Age: %d\n", name, age)// 使用 fmt.Scanln 读取输入fmt.Print("Enter your name: ")fmt.Scanln(&name)fmt.Printf("Name: %s\n", name)// 使用 fmt.Scanf 读取格式化输入fmt.Print("Enter your name and age (format: Name Age): ")fmt.Scanf("%s %d", &name, &age)fmt.Printf("Name: %s, Age: %d\n", name, age)// 使用 bufio 读取整行输入reader := bufio.NewReader(os.Stdin)fmt.Print("Enter a sentence: ")sentence, _ := reader.ReadString('\n')sentence = strings.TrimSpace(sentence)fmt.Printf("You entered: %s\n", sentence)
}

代码说明

  1. 输出部分
    • PrintPrintln 用于简单输出,后者会自动换行。
    • Printf 支持格式化动词,可以灵活控制输出格式。
    • Sprintf 返回格式化后的字符串,不直接打印。
    • Fprintf 将格式化内容写入 io.Writer,如文件。
  2. 输入部分
    • ScanScanln 用于读取标准输入,按空格或换行符分隔。
    • Scanf 支持格式化输入,适用于特定格式的数据读取。
    • bufio.NewReader 提供更高效的缓冲读取功能,适合读取整行输入。

四、Go语言 bufio 详解

bufio 是 “buffered I/O” 的缩写,它通过在 I/O 操作中引入缓冲区来提升性能。这个包包装了 io.Readerio.Writer 对象,创建了新的、带缓冲的 ReaderWriter 实例,提供了额外的功能。

4.1 为什么需要 bufio?(核心思想)

想象一下没有缓冲区的 I/O 操作,就像用一个非常小的勺子(比如一次只能装一个字节)从一个大水桶(文件或网络连接)里取水。每次取水都需要一个完整的“伸手-舀水-收回”的动作,这个过程的开销很大。
bufio 的作用就是引入一个“水杯”(缓冲区)。现在你的操作变成了:

  1. 用勺子从水桶里快速舀满一杯水(一次性读取一大块数据到缓冲区)。
  2. 需要用水时,直接从水杯里倒(从缓冲区读取数据给程序)。
    这个过程大大减少了“伸手-舀水-收回”的次数,从而显著提高了 I/O 效率,尤其是在处理大量小数据块或网络通信时。

核心优势:

  • 减少系统调用:将多次小的读/写操作合并为少数几次大的操作,减少了昂贵的系统调用次数。
  • 提供高级功能:除了性能提升,bufio 还提供了许多便捷的方法,如按行读取、按单词读取等。

4.2 bufio.Reader:带缓冲的读取器

bufio.Reader 是一个结构体,它包装了一个 io.Reader 接口。

1、创建 bufio.Reader:使用 bufio.NewReader 函数创建

func NewReader(rd io.Reader) *Reader

示例: 从标准输入创建一个缓冲读取器。

package main
import ("bufio""fmt""os"
)
func main() {// os.Stdin 实现了 io.Reader 接口reader := bufio.NewReader(os.Stdin)fmt.Printf("创建了一个缓冲区大小为 %d 字节的 Reader\n", reader.Size())
}

2、核心方法bufio.Reader 提供了一系列强大的读取方法。

a) ReadString(delim byte) - 按分隔符读取
这是最常用的方法之一。它会读取数据,直到遇到指定的分隔符 delim,然后返回包含分隔符在内的字符串。

func (b *Reader) ReadString(delim byte) (string, error)

示例: 逐行读取用户输入,直到用户输入 “exit”。

package main
import ("bufio""fmt""os""strings"
)
func main() {reader := bufio.NewReader(os.Stdin)fmt.Println("请输入内容(输入 'exit' 退出):")for {fmt.Print("> ")// 读取直到遇到换行符 '\n'input, err := reader.ReadString('\n')if err != nil {fmt.Println("读取错误:", err)return}// 去除字符串两端的空白字符(包括换行符)input = strings.TrimSpace(input)if input == "exit" {fmt.Println("程序退出。")break}fmt.Println("你输入了:", input)}
}

b) ReadBytes(delim byte) - 按分隔符读取字节切片
ReadString 类似,但返回的是 []byte 类型,在处理二进制数据或需要更高性能时非常有用。

func (b *Reader) ReadBytes(delim byte) ([]byte, error)

c) ReadLine() - 读取一行(不包含行尾符)
这是一个底层方法,它会读取一行,但不包含行尾的 \r\n。它返回三个值:line (内容), isPrefix (是否过长), err (错误)。通常我们更常用 ReadString('\n'),因为它更简单直观。

d) ReadSlice(delim byte) - 读取到分隔符的切片

这个方法返回的是对缓冲区内部数据的引用,而不是拷贝。这意味着下一次读取操作可能会覆盖这次返回的数据。它性能更高,但使用起来需要更小心。

e) Peek(n int) - 预览数据
Peek 返回缓冲区接下来的 n 个字节,但不会移动读取指针。也就是说,下次调用 Read 或其他读取方法时,仍然会从这次 Peek 的位置开始读。非常适合用于“偷看”一下接下来的内容是什么。

func (b *Reader) Peek(n int) ([]byte, error)

f) Read(p []byte) - 实现 io.Reader 接口
bufio.Reader 本身也实现了 io.Reader 接口。当你调用它的 Read 方法时,它会尝试从自己的缓冲区中填满你提供的字节切片 p。如果缓冲区空了,它会从底层的 io.Reader 中读取数据来填满缓冲区,然后再满足你的请求。

4.3 bufio.Writer:带缓冲的写入器

bufio.Writer 包装了一个 io.Writer 接口,将写入操作先存入内存缓冲区,当缓冲区满了或被显式刷新时,才一次性写入底层的 io.Writer

创建 bufio.Writer:使用 bufio.NewWriter 函数创建:

func NewWriter(w io.Writer) *Writer

示例: 创建一个写入标准输出的缓冲写入器。

package main
import ("bufio""fmt""os"
)
func main() {// os.Stdout 实现了 io.Writer 接口writer := bufio.NewWriter(os.Stdout)fmt.Printf("创建了一个缓冲区大小为 %d 字节的 Writer\n", writer.Size())
}

核心方法
a) Write(p []byte) - 写入字节切片
这是最核心的写入方法。它将数据写入缓冲区。如果缓冲区有足够空间,数据就暂存于此;如果空间不足,它会先将缓冲区的内容刷到底层 Writer,然后再写入新数据。
b) WriteString(s string) - 写入字符串
WriteString 是一个便捷方法,内部也是调用 Write
c) Flush() - 强制刷新
这是 bufio.Writer 最重要的方法。它会将缓冲区中所有(无论是否已满)的数据都写入到底层的 io.Writer 中。

为什么 Flush 如此重要?
因为如果不调用 Flush(),当程序结束时,缓冲区里可能还有未写入的数据,这些数据就会丢失!

示例: 向文件写入多行内容。

package main
import ("bufio""fmt""log""os"
)
func main() {// 1. 创建文件file, err := os.Create("output.txt")if err != nil {log.Fatal(err)}// 确保文件在函数结束时关闭defer file.Close()// 2. 创建缓冲写入器writer := bufio.NewWriter(file)// 3. 写入数据(此时数据在内存缓冲区中)fmt.Fprintln(writer, "这是第一行。")fmt.Fprintln(writer, "这是第二行。")writer.WriteString("这是第三行,使用 WriteString。\n")// 4. **必须刷新缓冲区,将数据写入文件**err = writer.Flush()if err != nil {log.Fatal("刷新缓冲区失败:", err)}fmt.Println("数据已成功写入 output.txt")
}

在这个例子中,如果注释掉 writer.Flush()output.txt 文件将是空的。

4.4 bufio.Scanner:更高级的读取工具

从 Go 1.1 开始,bufio 包引入了 Scanner 类型,它提供了一个更简单、更健壮的方式来读取数据,特别是按“token”(标记)分割数据。核心思想:
Scanner 的核心是 SplitFunc 类型的函数,它定义了如何将输入流分割成一个个标记。默认的分割函数是 ScanLines,即按行分割。

使用步骤

  1. 创建 Scanner: bufio.NewScanner(r io.Reader)
  2. (可选)设置分割函数: scanner.Split(customSplitFunc)
  3. 循环扫描: 使用 scanner.Scan() 作为循环条件,它会尝试读取下一个标记。
  4. 获取标记: 在循环体内,使用 scanner.Text() (返回 string) 或 scanner.Bytes() (返回 []byte) 获取当前标记的内容。
  5. 检查错误: 循环结束后,使用 scanner.Err() 检查是否发生了错误。

案例1:逐行读取文件
使用 Scanner 逐行读取文件比 Reader.ReadString 更简洁,且能更好地处理行尾符。

package main
import ("bufio""fmt""log""os""strings"
)
func main() {// 1. 打开文件file, err := os.Open("my_file.txt")if err != nil {log.Fatal(err)}defer file.Close()// 2. 创建 Scannerscanner := bufio.NewScanner(file)lineCount := 0wordCount := 0// 3. 循环扫描for scanner.Scan() {line := scanner.Text()lineCount++// 简单地按空格分割来计算单词数words := strings.Fields(line)wordCount += len(words)fmt.Printf("行 %d: %s\n", lineCount, line)}// 4. 检查错误if err := scanner.Err(); err != nil {log.Fatal("扫描时出错:", err)}fmt.Printf("\n扫描完成。共 %d 行,%d 个单词。\n", lineCount, wordCount)
}

案例2:自定义分割函数
Scanner 的强大之处在于可以自定义分割逻辑。例如,我们可以按单词读取。
示例: 使用 ScanWords 分割函数按单词读取。

// ... (前面的代码相同)
scanner := bufio.NewScanner(strings.NewReader("Hello world, this is a test."))
// 设置分割函数为按单词分割
scanner.Split(bufio.ScanWords)
for scanner.Scan() {fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {// ...
}

输出:

Hello
world,
this
is
a
test.

4.5 使用对比

类型适用场景优点注意事项
bufio.Reader需要精细控制读取过程,如读取固定字节数、预览数据、读取到特定分隔符。功能强大,控制力强,性能高。API 相对底层,使用时需注意错误处理。
bufio.Writer需要频繁进行小块写入,以减少系统调用,提高性能。显著提升写入性能。必须记得调用 Flush(),否则数据可能丢失。
bufio.Scanner需要方便地按行、按单词等“标记”来处理文本流。API 简洁易用,代码可读性高,错误处理方便。功能相对固定,不如 Reader 灵活。不适合读取二进制流。

Go语言的输入与输出格式化功能非常强大,通过 fmt 包和 bufio 包,可以满足各种输入输出需求。掌握这些函数和格式化动词,有助于编写清晰、易读的 Go 程序。

http://www.dtcms.com/a/445771.html

相关文章:

  • Go语言:数据压缩与解压详解
  • Odoo 前端控制器:构建无缝集成的网站页面
  • Go基础:json文件处理详解(标准库`encoding/json`)
  • 网站页头尺寸网站建设实物实训目的
  • RegNet:高效可扩展网络
  • 软考 系统架构设计师系列知识点之杂项集萃(169)
  • 大数据毕业设计选题推荐-基于大数据的人口普查收入数据分析与可视化系统-Hadoop-Spark-数据可视化-BigData
  • 实验室网站制作数据交易网站源码
  • 【Kubernetes】(二十)Gateway
  • 爬虫与自动化技术深度解析:从数据采集到智能运维的完整实战指南——企业级实时数据闭环构建
  • 桂林哪里可以做网站wordpress前台不显示
  • 模拟退火粒子群优化算法(SA-PSO):原理、应用与展望
  • 不用每次都改 `easysearch.yml` 也能改启动参数 —— 用 Docker 环境变量搞定一切
  • 三问岚图,计划登陆港股对消费者意味着什么?
  • 舒尔特方格开源
  • D365财务和运营应用
  • 沧州seo公司哈尔滨seo和网络推广
  • 5.机器学习的介绍
  • 安徽合肥网站制作公司源代码
  • Flink 连接器与格式thin/uber 制品、打包策略与上线清单
  • 玩转ClaudeCode:通过Chrome DevTools MCP实现页面抓取和调试的基础入门
  • Playwright MCP vs Chrome DevTools MCP vs Chrome MCP 深度对比
  • 网页 网站 区别哪些网站可以免费申请
  • 玩转ClaudeCode:通过Chrome DevTools MCP实现智能页面抓取与调试
  • rabbitMQ续谈
  • RabbitMQ概念 与 工作原理
  • 力扣每日一题(一)双指针 + 状态转移dp 矩阵快速幂
  • [ Redis ] 数据结构储存系统
  • 广东网站开发推荐山东住房城乡建设厅网站首页
  • [人工智能-综述-21]:学习人工智能的路径