【Go】Go文件操作详解
1. 前言
相信如果看过之前文章的朋友们一定知道我想讲什么了?灵魂三问:文件是什么?为什么需要文件?文件怎么操作?前面章节我们已经能够编写各种各样的功能代码了,但是一个很现实的问题就是我们没有任何 持久化存储 的能力!也就是说我们之前编写的任何代码中的数据都保存在内存中(内存具有掉电易失性),举一个例子:我们想开发一款魔塔游戏,要求拥有存档和读档的能力!我们就可以使用文件的读写操作来实现持久化功能
2. 编码小历史
由于文件操作涉及到编码规则以及字符集的影响,因此我将先介绍有关编码的历史发展脉络,编码集的发展阶段大致可以总结如下:ASCII -> GBK -> Unicode -> UTF-8
- ASCII编码
众所周知,计算机起源于美国,对于老美来说只有26个英文字母,算上各种特殊符号也绝不会超过128个,因此表示一个字符使用7个比特位即可(2^7 = 128)因此使用1个字节来存储一个字符是绰绰有余的,因此诞生了赫赫有名的 “ASCII” 编码字符集
- GBK编码
但是当计算机发展到东亚国家以后就不同了,中文、韩文、日文的字符也想在计算机中显示怎么办?哪怕是常见的中文也有几千个(1个字节肯定是存不下了)因此引入了扩展的中文字符集——GBK编码,使用2个字节表示一个字符,同时还必须保证兼容ASCII编码集。两个字节足以表示65536个字符,应对当前场景应该是没问题了
- Unicode编码
随着计算机不断在世界范围内普及,每个国家都进行扩展,造成的后果就是很难统一管理维护。于是ISO国际化标准组织收集了世界范围内的字符,编写了"万国码"——Unicode编码,该种编码方式对于任何一个字符都使用两个字节进行存储:表示范围为65536个字符
- UTF-8编码
虽然Unicode编码已经可以达到统一的效果了,但是老美不乐意了:原本只需要使用一个字节表示的a、b、c现在全部需要扩大两倍存储空间,这不闹吗???最后诞生了"UTF-8"字符集(可以与unicode相互转化),最终的效果就是对于ASCII码表字符使用1个字节存储,对于中文统一使用3个字节存储,其余文字字符各有对应规则
3. Go的字符和字节
在我看来字符就是字节比特流(一串二进制数),两者是等价的:比如 ‘a’ <=> 97,在对应的UTF-8规则集下两者具有对应关系,在Go语言当中字符类型可以有如下两种声明方式:
- 语法格式:
var ch byte
或者var ch uint8
表示该字符占用1个字节 - 语法格式:
var ch rune
或者var ch int32
表示该字符占用4个字节
使用1个字节表示字符:
func main() {// 声明方式1var ch1 bytech1 = 'a'fmt.Println(ch1)var ch2 uint8ch2 = 'b'fmt.Println(ch2)
}
使用4个字节表示字符:
func main() {// 声明方式2var ch3 runech3 = '米'fmt.Println(ch3)var ch4 int32ch4 = '饭'fmt.Println(ch4)
}
💡 注意:在Go语言源码层面byte类型与uint8类型是一样的,rune类型与int32类型是一样的(如果追踪源码会发现如下语句:type byte = uint8; type rune = int32)
4. 字符串
4.1 字符串存储原理
Go语言当中的string数据类型占用16个字节,其中前8个字节是一个指针,存储底层字节数组的起始地址;后8个字节是一个整数,存储字符串的长度(以字节为单位)
string对应数据结构实现可以在源码包src/runtime/string.go
中看到:
- str:指向底层字节数组的地址
- len:底层字节数组长度,可通过
len
内置函数获取
4.2 字符串的遍历
字符串遍历有如下两种方式:
- 使用
len
函数按照长度进行遍历
func main() {// 字符串遍历方式1:按照len遍历var str = "米饭666"for i := 0; i < len(str); i++ {fmt.Printf("%d, %c\n", str[i], str[i])}
}
代码运行结果:
- 使用
range
迭代遍历
func main() {// 字符串遍历方式2:按照range遍历for _, v := range str {fmt.Printf("%d, %c\n", v, v)}
}
代码运行结果:
💡 总结:Go语言当中的字符串使用len遍历时,获取的是每一个字节的内容(若包含中文则会出现乱码结果);当使用range进行遍历时,内部会帮助解析成unicode字符
4.3 字符串和字节串的转化
由于字符串的底层是一个字节数组,因此字符串和字节串存在相互转化的关系:
- 字符串转字节串:
字节数组 = []byte(字符串)
func main() {// 字符串转字节串var str = "米饭666"var byteArr = []byte(str)fmt.Println(byteArr)
}
代码运行结果:
- 字节串转字符串:
字符串 = string(字节数组)
func main() {// 字节串转字符串var byteArr = []byte{231, 177, 179, 233, 165, 173, 54, 54, 54}var str = string(byteArr)fmt.Println(str)
}
代码运行结果:
5. 读写文件
5.1 读文件
首先我们在当前main.go同级创建文件file.txt
内容如下:
我们可以通过file, err := os.Open(file_path)
打开一个文件,如果打开成功则返回对应的文件句柄
5.1.1 方式1:使用Read方法
语法格式:n, err := file.Read(空字节切片)
- 如果读取失败则会将错误内容写入err中
- 如果读取成功那么n就是读取的字节数,读取内容会存放到空字节切片中
func main() {// 打开文件file, err := os.Open("./file.txt")if err != nil {fmt.Println("打开文件失败, err:", err)}// 关闭文件句柄defer file.Close()var data = make([]byte, 1024)n, err := file.Read(data)if err != nil {fmt.Println("读取文件失败. err:", err)}fmt.Printf("读取到的字节数为:%d\n", n)fmt.Printf("读取内容为:%s\n", string(data[:n]))
}
代码运行结果:
5.1.2 方式2:使用ReadString方法
上述方式是按照字节进行读取的,我们可以使用内置包bufio.NewReader().ReadString
按照指定分隔符进行读取(比如换行符\n读取一行)
func main() {// 打开文件file, err := os.Open("./file.txt")if err != nil {fmt.Println("打开文件失败!", err)}defer file.Close()// 读取文件reader := bufio.NewReader(file)for true {line, err := reader.ReadString('\n')if err == io.EOF {// 读取到结束fmt.Print(line)break}fmt.Print(line)}
}
代码运行结果:
5.1.3 方式3:使用ReadFile方法
我们可以使用ioutil.ReadFile(file_path)
方法直接读取整个文件内容,适用于小型文件
func main() {// 读取文件content, err := ioutil.ReadFile("./file.txt")if err != nil {fmt.Println("读取文件失败!", err)}fmt.Println(string(content))
}
代码运行结果:
5.2 写文件
我们可以通过file, err := os.OpenFile(file_path, flag, fileMode)
更一般性的打开一个文件
- file_path:文件所在路径
- flag:打开方式标记,比如os.O_CREATE:文件不存在则新建,os.O_REONLY:只读模式,os.O_APPEND:追加写入模式
- fileMode:文件权限:例如0777表示 rwxrwxrwx
5.2.1 方式1:使用Write方法
func main() {// 一般性的打开文件file, err := os.OpenFile("./file.txt", os.O_CREATE|os.O_WRONLY, 0666)if err != nil {fmt.Println("打开文件失败!", err)}defer file.Close()// Write写入字节数组file.Write([]byte("Hello World\nHello ricejson\n"))
}
5.2.2 方式2:使用WriteString方法
func main() {// 一般性的打开文件file, err := os.OpenFile("./file.txt", os.O_CREATE|os.O_WRONLY, 0666)if err != nil {fmt.Println("打开文件失败!", err)}defer file.Close()// WriteString写入字符串writer := bufio.NewWriter(file)writer.WriteString("Hello world\n Hello ricejson\n")// 强制刷新缓冲区writer.Flush()
}
5.2.3 方式3:使用WriteFile方法
func main() {// WriteFile直接写文件ioutil.WriteFile("file.txt", []byte("Hello World\n"), 0666)
}
相关文章:

【Go】Go文件操作详解
1. 前言 相信如果看过之前文章的朋友们一定知道我想讲什么了?灵魂三问:文件是什么?为什么需要文件?文件怎么操作?前面章节我们已经能够编写各种各样的功能代码了,但是一个很现实的问题就是我们没有任何 持…...

[react+ts] useRef获取自定义组件dom或方法声明
想用useRef获取自定义组件? 如果获取dom,直接写 const sonRef useRef<HTMLDivElement>(null); 然后子组件用forwardRef包一层,注意是HTMLDivElement,别写错, 写HTMLElement不行 const Son forwardRef<HTMLDivElement, IProps>((props, ref) > {}) 切记这…...

AI 将在今年获得“永久记忆”,2028美国会耗尽能源储备
AI的“永久记忆”时代即将来临 谷歌前CEO施密特揭示了AI技术的前景,他相信即将在2025年迎来一场伟大的变化。AI将实现“永久记忆”,改变我们与科技的互动过程。施密特将现有的AI上下文窗口比作人类的短期记忆,难以持久保存信息。他的设想是…...

【视频笔记】基于PyTorch从零构建多模态(视觉)大模型 by Umar Jamil【持续更新】
视频链接: 基于PyTorch从零构建多模态(视觉)大模型 by Umar Jamil 从头编写一个视觉语言模型:PloyGamma,是谷歌的一个模型 1:原始图像 2:视觉编码器(本文是viT),通过对比学习进行训练。这个对比学习最开始是CLIP,后来被谷歌改成了SigLIP 3:线性投影层 4:如何将图…...
解决 C++ 中头文件相互引用和解耦问题
在 C 中,当多个 .h 文件相互引用时,可能会导致 循环依赖 或 头文件冗余 问题,进而引发编译时间延迟、代码复杂度增加等问题。为了有效地解耦和组织代码,可以采用以下几种策略和思想: 1. 前向声明(Forward …...

河马剧场(短剧)APP的邀请码怎么填写
上篇给大家说到河马剧场免费看短剧还能领5.2元3天vip会员,本文就说一下河马剧场河马短剧APP的邀请码怎么填写。 河马短剧APP填写邀请码分三步: 1、安装登陆河马短剧APP 2、点击底部导航栏中间的“福利” 3、往下划会看到“填写邀请码领3天vip” 4、…...

01:C语言的本质
C语言的本质 1、ARM架构与汇编2、局部变量初始化与空间分配2.1、局部变量的初始化2.1、局部变量数组初始化 3、全局变量/静态变量初始化化与空间分配4、堆空间 1、ARM架构与汇编 ARM简要架构如下:CPU,ARM(能读能写),Flash(能读&a…...
第1章:数据库基础
第1章:数据库基础 1.1 数据库概述 1.1.1 什么是数据库 数据库的定义数据库的发展历程数据库的重要性 1.1.2 关系型数据库简介 关系型数据库模型常见的关系型数据库关系型数据库的特点 1.1.3 MySQL在企业中的应用 Web应用电商平台金融系统大数据存储 1.2 数据…...
C++教程 | string类的定义和初始化方法
在C中,string是标准库中用于处理字符串的类,定义在 头文件中,它提供了方便、灵活的字符串操作功能。以下是一些常见的定义和初始化string对象的方法: 1. 默认初始化 可以直接定义一个空的string对象,语法如下&#x…...

React中的合成事件
合成事件与原生事件 区别: 1. 命名不一样,原生用纯小写方式,react用小驼峰的方式 原生:onclick React的:onClick 2. 事件处理函数的写法不一样 原生的是传入一个字符串,react写法传入一个回调函数 3.…...

[SMARTFORMS] 创建FORM
输入事务码SMARTFORMS进入表单开发界面,选中表单,自定义表单名称ZFS_DEMO_2025 点击"创建"按钮,跳转至"SAP表格设计器"页面 在"表格属性"填写表单描述、指定页格式和样式 在"表格接口"可以填写SMART…...
成都和力九垠科技有限公司九垠赢系统Common存在任意文件上传漏洞
免责声明: 本文旨在提供有关特定漏洞的深入信息,帮助用户充分了解潜在的安全风险。发布此信息的目的在于提升网络安全意识和推动技术进步,未经授权访问系统、网络或应用程序,可能会导致法律责任或严重后果。因此,作者不对读者基于本文内容所采取的任何行为承担责任。读者在…...

基于Python的考研学习系统
作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏:…...
『SQLite』几种向表中插入数据的方法
向表中插入数据 INSERT INTO 语句用来给数据库中的某个表中新增数据行。 案例 直接根据基本语法插入数据插入时不用全部指定列名方式根据查询结果将数据插入另一张表中 注意 上述内容详讲见文章:SQLite的INSERT操作(内含案例)...

什么是Kafka的重平衡机制?
Kafka 的重平衛机制是指在消费者组中新增或删除消费者时,Kafka 集群会重新分配主题分区给各个消费者,以保证每个消费者消费的分区数量尽可能均衡。 重平衡机制的目的是实现消费者的负载均衡和高可用性,以确保每个消费者都能够按照预期的方式…...

pdf预览 报:Failed to load module script
pdf 预览报: Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of “application/octet-stream”. Strict MIME type checking is enforced for module scripts per HTML spec. 报错原因:…...

AI 角色扮演法的深度剖析与实践
📢📢📢 大家好,我是云楼Yunlord,CSDN博客之星人工智能领域前三名,多年人工智能学习工作经验,一位兴趣稀奇古怪的【人工智能领域博主】!!!😜&#…...

weblogic问题
安装weblogic单机后启动weblogic进程: 第一行: 这是一个 su 命令,用于切换到 weblogic 用户。 第二行: 这是 weblogic 用户的 bash shell 会话。 第三行: 这是启动 WebLogic 服务器的脚本。 第四行: 这是 …...
Qt仿音乐播放器:客户端唯一化
一、铺垫 1.我们采用共享内存来进行客户端的唯一化; 2.我刚看到的时候,就感觉,这是人想出来的吗?太绝了 二、实例 int main(int argc, char *argv[]) {QApplication a(argc, argv);QSharedMemory shareMemory("Widget&qu…...

ceph文件系统
ceph文件系统:高度可扩展,分布式的存储文件系统,旨在提高性能,高可靠性和高可用的对 象存储,块存储,文件系统的存储。使用分布式的算法保证数据的高可用和一致性。 ceph的组件 1、MON:ceph m…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...

前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...

AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)
前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 …...
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析 一、第一轮基础概念问题 1. Spring框架的核心容器是什么?它的作用是什么? Spring框架的核心容器是IoC(控制反转)容器。它的主要作用是管理对…...
comfyui 工作流中 图生视频 如何增加视频的长度到5秒
comfyUI 工作流怎么可以生成更长的视频。除了硬件显存要求之外还有别的方法吗? 在ComfyUI中实现图生视频并延长到5秒,需要结合多个扩展和技巧。以下是完整解决方案: 核心工作流配置(24fps下5秒120帧) #mermaid-svg-yP…...
鸿蒙(HarmonyOS5)实现跳一跳小游戏
下面我将介绍如何使用鸿蒙的ArkUI框架,实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...

云安全与网络安全:核心区别与协同作用解析
在数字化转型的浪潮中,云安全与网络安全作为信息安全的两大支柱,常被混淆但本质不同。本文将从概念、责任分工、技术手段、威胁类型等维度深入解析两者的差异,并探讨它们的协同作用。 一、核心区别 定义与范围 网络安全:聚焦于保…...

uni-app学习笔记三十五--扩展组件的安装和使用
由于内置组件不能满足日常开发需要,uniapp官方也提供了众多的扩展组件供我们使用。由于不是内置组件,需要安装才能使用。 一、安装扩展插件 安装方法: 1.访问uniapp官方文档组件部分:组件使用的入门教程 | uni-app官网 点击左侧…...