Go学习第十章——文件操作,Json和测试

Go文件操作,Json和测试

      • 1 文件
        • 1.1 基本介绍
        • 1.2 读取的基本操作
        • 1.3 写入的基本操作
        • 1.4 使用案例(三个)
      • 2 Go语言的Json使用
        • 2.1 序列化案例
        • 2.2 反序列化案例
      • 3 单元测试
        • 3.1 先看个需求
        • 3.2 快速入门
        • 3.3 入门总结

1 文件

1.1 基本介绍

文件在程序中是以流的形式来操作的。

image-20231026145329310

**流:**数据在数据源(文件)和程序(内存)之间经历的路径。

**输入流(读文件):**数据从数据源(文件)到程序(内存)的路径。

**输出流(写文件):**数据从程序(内存)到数据源(文件)的路径。

在Golang里,os.File封装所以文件相关操作,File是一个结构体。

// File represents an open file descriptor.
type File struct {
	*file // os specific
}
1.2 读取的基本操作

方法一:使用带缓存的方式读取,适用于大文件读取

读取文件需要先了解下面的几个方法函数,需要这四步才算是一个完整的读取操作。

  1. 使用 os.Open() 函数打开文件

    • 函数原型:func Open(name string) (*os.File, error)
    • 示例代码:
    file, err := os.Open("test.txt")
    if err != nil {
        log.Fatal(err)
    }
    

    完整代码:

    func main() {
    	// 1.file 被叫做 file对象,file指针 ,file文件句柄
    	file, err := os.Open("D:\\Desktop\\test.txt")
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	// 输出文件
    	fmt.Printf("file=%v", file)
    
    	// 关闭文件
    	err = file.Close()
    	if err != nil {
    		fmt.Println("close file err=", err)
    	}
    }
    
    

    输出结果:file=&{0xc00009aa00}

  2. 使用 bufio.NewReader() 函数创建读取缓冲区

    • 函数原型:func NewReader(rd io.Reader) *bufio.Reader
    • 示例代码:
    reader := bufio.NewReader(file) // 默认缓冲区为4096
    
  3. 使用 ReadString() 函数读取文件内容

    • 函数原型:func (b *bufio.Reader) ReadString(delim byte) (string, error)
    • 示例代码:
    content, err := reader.ReadString('\n')
    if err != nil && err != io.EOF {
        log.Fatal(err)
    }
    
  4. 关闭文件

    • 示例代码:
    file.Close()
    

将上面的步骤合起来,就是读取文件的全过程:

func main() {
	// 1.file 被叫做 file对象,file指针 ,file文件句柄
	file, err := os.Open("D:\\Desktop\\test.txt")
	if err != nil {
		log.Fatal(err)
	}

	// 输出文件
	fmt.Printf("file=%v \n", file)

	// 关闭文件(defer 最后结束再执行)
	defer func(file *os.File) {
		err := file.Close()
		if err != nil {
			fmt.Println("close file err=", err)
		}
	}(file)
	
	// 创建读取缓冲区
	reader := bufio.NewReader(file)
	
	// 读取缓冲区内容,也就是文件内容
	content, err := reader.ReadString('\n')
	if err != nil && err != io.EOF {
		log.Fatal(err)
	}
	
	// 打印
	fmt.Printf("content=%v", content)
}

输出结果:

file=&{0xc000078a00}
content=你好,Hello Go File!!

方法二:一次性将文件读取到内存中,适用于文件不大的情况

  1. 使用 ReadFile() 函数读取文件内容,读取整个文件的操作已经封装在函数内,不用手动打开或关闭文件。。
  • 函数原型:func ReadFile(name string) ([]byte, error)
  • 示例代码:
data, err := io.ReadFile("test.txt")
if err != nil {
    log.Fatal(err)
}

代码示例:

func main() {
    // 一次性读取文件
    data, err := os.ReadFile("D:\\Desktop\\test.txt")
    if err != nil {
       log.Fatal(err)
    }

    fmt.Println(string(data))
}

输出结果:

你好,Hello Go File!!
  1. 使用 io.ReadAll() 时,需要先手动打开文件,并在读取完成后手动关闭。(不推荐,已经被舍弃
  • 函数原型:func ReadFile(name string) ([]byte, error)
  • 示例代码:
func main() {
    file, err := os.Open("D:\\Desktop\\test.txt")
    if err != nil {
       log.Fatal(err)
    }
    defer file.Close()

    data, err := ioutil.ReadAll(file)
    if err != nil {
       log.Fatal(err)
    }

    fmt.Println(string(data))
}

输出结果:

你好,Hello Go File!
1.3 写入的基本操作
  1. 使用 os.Create() 函数创建文件

    • 函数原型:func Create(name string) (*os.File, error)
    • 示例代码:
    file, err := os.Create("output.txt")
    if err != nil {
        log.Fatal(err)
    }
    
  2. 使用 file.Write() 函数将字符串写入文件

  • 函数原型:func (f *File) Write(b []byte) (n int, err error)
  • 示例代码:
_, err := file.Write([]byte("Hello, world!\n"))
if err != nil {
    log.Fatal(err)
}
  1. 关闭文件
  • 示例代码:
file.Close()

将上面的步骤合起来,就是创建并写入文件的全过程:

func main() {
	// 创建文件
	file, err := os.Create("D:\\Desktop\\output.txt")
	if err != nil {
		log.Fatal("创建文件错误",err)
	}
	// 将字符串写入文件
	_, err = file.Write([]byte("Hello, world!\n"))
	if err != nil {
		log.Fatal("文件写入错误:", err)
	}
	err = file.Close()
	if err != nil {
		fmt.Println("关闭文件错误:", err)
	}
}

输出结果:无。打开文件,就能发现写入成功啦~~~~

1.4 使用案例(三个)

**案例一:**将一个文件的内容,写到另一个文件,注意,两个文件都已经存在。

方案一:通过缓存的方式

func main() {
    // 打开原文件
    inputFile, err := os.Open("D:\\Desktop\\input.txt")
    if err != nil {
       log.Fatal(err)
    }
    defer inputFile.Close()

    // 创建目标文件
    outputFile, err := os.Create("D:\\Desktop\\output.txt")
    if err != nil {
       log.Fatal(err)
    }
    defer outputFile.Close()

    // 创建缓冲区
    buffer := make([]byte, 1024)

    // 读取原文件并写入目标文件
    for {
       // 从原文件读取数据到缓冲区
       n, err := inputFile.Read(buffer)
       if err != nil && err != io.EOF {
          log.Fatal(err)
       }
       if n == 0 {
          break
       }

       // 将数据从缓冲区写入目标文件
       _, err = outputFile.Write(buffer[:n])
       if err != nil {
          log.Fatal(err)
       }
    }

    log.Println("文件内容写入成功!")
}

方法二:使用io.Copy()复制io流

func main() {
    // 打开原文件
    inputFile, err := os.Open("D:\\Desktop\\input.txt")
    if err != nil {
       log.Fatal(err)
    }
    defer inputFile.Close()

    // 创建目标文件
    outputFile, err := os.Create("D:\\Desktop\\output.txt")
    if err != nil {
       log.Fatal(err)
    }
    defer outputFile.Close()

    // 将原文件内容写入目标文件
    _, err = io.Copy(outputFile, inputFile)
    if err != nil {
       log.Fatal(err)
    }

    log.Println("文件内容写入成功!")
}

输出结果:2023/10/26 16:29:29 文件内容写入成功!

**案例二:**将一个图片拷贝到另一个文件夹下

func main() {
    srcPath := "D:\\Desktop\\img.jpg"
    destPath := "D:\\Desktop\\img\\image.jpg"

    err := copyFile(srcPath, destPath)
    if err != nil {
       fmt.Println("Failed to copy file:", err)
       return
    }

    fmt.Println("File copied successfully!")
}

func copyFile(srcPath, destPath string) error {
    srcFile, err := os.Open(srcPath)
    if err != nil {
       return err
    }
    defer srcFile.Close()

    destFile, err := os.Create(destPath)
    if err != nil {
       return err
    }
    defer destFile.Close()

    _, err = io.Copy(destFile, srcFile)
    if err != nil {
       return err
    }

    return nil
}

输出结果:File copied successfully!

上述代码中,srcPath和destPath分别表示源图片文件的路径和目标文件夹的路径。

在copyFile函数中,首先使用os.Open打开源图片文件,并使用os.Create创建目标文件。然后使用io.Copy将源图片文件的内容拷贝到目标文件中。最后返回nil表示拷贝成功,或者返回拷贝过程中遇到的错误。

**案例三:**统计一个文件内容里的英文、数字、空格和其他字符数量

func main() {
    filePath := "D:\\Desktop\\input.txt" // 文件路径

    data, err := os.ReadFile(filePath)
    if err != nil {
       fmt.Printf("读取文件失败:%s\n", err)
       return
    }

    charsCount := make(map[string]int)
    for _, ch := range string(data) {
       switch {
       case unicode.IsLetter(ch):
          charsCount["英文"]++
       case unicode.IsDigit(ch):
          charsCount["数字"]++
       case unicode.IsSpace(ch):
          charsCount["空格"]++
       default:
          charsCount["其他字符"]++
       }
    }

    fmt.Printf("英文字符数量:%d\n", charsCount["英文"])
    fmt.Printf("数字字符数量:%d\n", charsCount["数字"])
    fmt.Printf("空格数量:%d\n", charsCount["空格"])
    fmt.Printf("其他字符数量:%d\n", charsCount["其他字符"])
}

输出结果:

英文字符数量:652
数字字符数量:67
空格数量:133
其他字符数量:107

2 Go语言的Json使用

JSON易于机器解析和生成,并有效地提升网络传输效率通常程序在网络传输时会先将数据(结构体、map等)序列化成son字符串到接收方得到ison字符串时,在反序列化恢复成原来的数据类型(结构体、map等)。这种方式已然成为各个语言的标准。

image-20231026165248054

2.1 序列化案例
type Person struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func main() {
	people := []map[string]interface{}{
		{
			"name": "Alice",
			"age":  25,
		},
		{
			"name": "Bob",
			"age":  30,
		},
	}

	data, err := json.Marshal(people)
	if err != nil {
		fmt.Printf("序列化失败: %s", err)
		return
	}

	err = ioutil.WriteFile("people.json", data, 0644)
	if err != nil {
		fmt.Printf("写入文件失败: %s", err)
		return
	}

	fmt.Println("JSON数据已写入文件")
}

在代码中,我们定义了一个Person结构体,表示每个人的姓名和年龄,这里主要添加tag,不然序列化后的是大写,不符合公共规范。然后,我们创建了一个包含多个map的切片people,每个map对应一个人的信息。

使用json.Marshal()函数将切片people序列化为JSON数据。json.Marshal()函数会返回一个[]byte类型的字节切片,表示JSON数据。

然后,我们使用ioutil.WriteFile()函数将JSON数据写入一个名为people.json的文件。

最后,我们输出一个提醒信息,表示JSON数据已成功写入文件。

运行以上代码,会在当前目录下生成一个名为people.json的文件,其中包含以下JSON数据:

[
    {"name":"Alice","age":25},
    {"name":"Bob","age":30}
]

这个JSON数组包含了两个map,每个map对应一个人的姓名和年龄。

2.2 反序列化案例

将上面的代码反过来,json格式转换成对应的数据

type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

func main() {
    filePath := "D:\\Desktop\\people.json" // JSON文件路径

    // 读取JSON文件内容
    data, err := os.ReadFile(filePath)
    if err != nil {
       fmt.Printf("读取文件失败:%s\n", err)
       return
    }

    var people []Person

    // 反序列化JSON数据
    err = json.Unmarshal(data, &people)
    if err != nil {
       fmt.Printf("反序列化失败: %s\n", err)
       return
    }

    fmt.Printf("解析到%d个人的信息:\n", len(people))
    for _, p := range people {
       fmt.Printf("姓名:%s\t年龄:%d\n", p.Name, p.Age)
    }

    // 将JSON数据反序列化为map类型
    var peopleMap []map[string]interface{}

    err = json.Unmarshal(data, &peopleMap)
    if err != nil {
       fmt.Printf("反序列化为map失败: %s\n", err)
       return
    }

    fmt.Printf("解析到%d个人的信息:\n", len(peopleMap))
    for _, p := range peopleMap {
       fmt.Printf("姓名:%s\t年龄:%v\n", p["name"], p["age"])
    }
}

输出结果:

解析到2个人的信息:
姓名:Alice     年龄:25
姓名:Bob       年龄:30
解析到2个人的信息:
姓名:Alice     年龄:25
姓名:Bob       年龄:30

3 单元测试

3.1 先看个需求

在我们工作中,我们会遇到这样的情况,就是去确认一个函数,或者一个模块的结果是否正确,如下:

func addUpper(n int) int {
	res := 0
	for i := 1; i <= n; i++ {
		res += i
	}
	return res
}

在 main 函数中,调用 addUpper 函数,看看实际输出的结果是否和预期的结果一致,如果一致,则说明函数正确,否则函数有错误,然后修改错误.

传统方式的缺点

  1. 不方便,我们需要在 main 函数中去调用,这样就需要去修改 main 函数,如果现在项目正在运行,就可能去停止项目。
  2. 不利于管理,因为当我们测试多个函数或者多个模块时,都需要写在 main 函数,不利于我们管理和清晰我们思路
  1. 引出单元测试。-> testing 测试框架 可以很好解决问题。
3.2 快速入门

Go 语言中自带有一个轻量级的测试框架 testing 和自带的 go test 命今来实现单元测试和性能测试,testing 框架和其他语言中的测试框架类似,可以基于这个框架写针对相应函数的测试用例,也可以基
于该框架写相应的压力测试用例。通过单元测试,可以解决如下问题:

  1. 确保每个函数是可运行,并且运行结果是正确
  2. 确保写出来的代码性能是好的,
  3. 单元测试能及时的发现程序设计或实现的逻辑错误,使问题及早暴露,便于问题的定位解决,

image-20231026171938573

1. 创建一个cal.go文件,把需要测试的代码放在里面

package test01

func AddUpper(n int) int {
    res := 0
    for i := 1; i <= n; i++ {
       res += i
    }
    return res
}

2. 创建cal_test.go文件,在里面写测试案例

import (
	"testing"
)

func TestAddUpper(t *testing.T) {
	res := AddUpper(10)
	if res != 55 {
		// fmt.Printf("AddUpper(10) 执行错误,期望值=%v 实际值=%v\n", 55, res)
		t.Fatalf("AddUpper(10) 执行错误,期望值=%v 实际值=%v\n", 55, res)
	}

	// 如果正确,输出日志
	t.Logf("AddUpper(10) 执行正确。。。")
}

执行后的结果如图所示,我使用的GoLand,就比较方便:

image-20231026173326882

3.3 入门总结
  1. 测试用例文件名必须以 test.go 结尾。 比如 cal test.go ,cal 不是固定的。

  2. 测试用例函数必须以 Test 开头,一般来说就是 Test+被测试的函数名,比如 TestAddUpper

  3. TestAddUpper(t *tesing.T) 的形参类型必须是 *testing.T

  4. 一个测试用例文件中,可以有多个测试用例函数,比如 TestAddUpper、TestSub

  5. 运行测试用例指令
    (1) cmd>go test [ 如果运行正确,无日志,错误时,会输出日志 ]

(2) cmd>go test-v [ 运行正确或是错误,都输出日志 ]

  1. 当出现错误时,可以使用 t.Fatalf 来格式化输出错误信息,并退出程序

  2. t.Logf 方法可以输出相应的日志

  3. 测试用例函数,并没有放在 main 函数中,也执行了,这就是测试用例的方便之处

  4. PASS 表示测试用例运行成功,FAIL 表示测试用例运行失败

  5. 测试单个文件,一定要带上被测试的原文件
    go test -v cal test.go cal.go

  6. 测试单个方法
    go test -v -test.runTestAddUpper

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/105417.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用Jenkins触发gitlab的webhook

满足条件&#xff1a; 首先手动构建可以完成构建 例如&#xff1a; 打开项目点击配置 在“Build Triggers”栏勾选&#xff0c;Build when a change is pushed to GitLab. GitLab webhook &#xff1b;如下 复制URL链接&#xff0c;我的链接是&#xff1a;http://192.168.44…

HarmonyOS鸿蒙原生应用开发设计- 流转图标

HarmonyOS设计文档中&#xff0c;为大家提供了独特的流转图标&#xff0c;开发者可以根据需要直接引用。 开发者直接使用官方提供的流转图标内容&#xff0c;既可以符合HarmonyOS原生应用的开发上架运营规范&#xff0c;又可以防止使用别人的图标侵权意外情况等&#xff0c;减…

【每日一题】切割后面积最大的蛋糕

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;排序 其他语言python3 写在最后 Tag 【排序】【数组】【2023-10-27】 题目来源 1465. 切割后面积最大的蛋糕 题目解读 切割后面积最大的蛋糕。 解题思路 方法一&#xff1a;排序 本题较为简单&#xff0c;找出最大…

Android加载SO包

一、前言 这几天用Android整合开源的RTMP推拉流都没成功&#xff0c;好几年没玩Android了碰到好多坑&#xff0c;在Android中为了效率难免需要调用C语言编写生成的SO文件&#xff0c;比如图片渲染加速&#xff0c;视频编解码等插件&#xff0c;今天我们就先聊一下在Android中如…

51单片机实验:数码管动态显示00-99

1、实验要求 利用STC89C52RC单片机开发板实现&#xff1a;使用2位数码管循环显示00-99&#xff0c;每次间隔1s&#xff0c;并且当计数到20时&#xff0c;则蜂鸣器鸣响1次。 2、实验分析 程序实现分析&#xff1a; 1、定义数码管位选引脚&#xff08;P2.4、P2.5、P2.6、…

ES6初步了解迭代器

迭代器是什么&#xff1f; 迭代器(iterator)是一种接口&#xff0c;为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 iterator 接口&#xff0c;就可以完成遍历操作 ES6创造了一种新的遍历方法for…of循环&#xff0c;iterator 接口主要供 for…of 使用 原生中具…

Android12之#pragma clang diagnostic ignored总结(一百六十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

pip 更换源

方案1 在C盘用户名录下新建pip文件夹&#xff0c;里面包含pip.ini文件 方案2 在C盘用户名目录的AppData的Roaming下新建pip文件夹&#xff0c;里面包含pip.ini文件。 内容为 [global] index-url https://pypi.tuna.tsinghua.edu.cn/simple

Git(四)底层命令:git对象、树对象、提交对象

目录 一、知识回顾1.1 Linux 基础命令1.2 .git 文件夹解析 二、git 对象&#xff08;数据对象&#xff09;2.1 hash-object 存储对象2.2 cat-file 查看对象 三、树对象3.1 ls-files 查看暂存区3.2 update-index 创建暂存区3.3 write-tree 生成树对象3.4 更新暂存区&#xff0c;…

ffmpeg的下载和编译(vs2022)

感谢大佬的二创,直接提供了sln编译 ffmpeg二创地址 创建如下目录 build存放代码(build最好改成source,因为作者这么建议,编译完才发现) msvc存放第三方依赖的头文件,这里固定叫msvc,因为大佬的sln里查找的路径是这个,不嫌麻烦也可以自己改 下载代码和编译器 下载源码…

[论文阅读]Point Density-Aware Voxels for LiDAR 3D Object Detection(PDV)

PDV Point Density-Aware Voxels for LiDAR 3D Object Detection 论文网址&#xff1a;PDV 论文代码&#xff1a;PDV 简读论文 摘要 LiDAR 已成为自动驾驶中主要的 3D 目标检测传感器之一。然而&#xff0c;激光雷达的发散点模式随着距离的增加而导致采样点云不均匀&#x…

层次式架构的设计理论与实践

层次式架构的设计理论与实践 层次式架构概述 层次式架构的定义和特性 定义 特性 层次式架构的一般组成(表现层、中间层、数据访问层和数据层) 表现层框架设计 设计模式 MVC MVP MVVM XML技术 UIP设计思想 表现层动态生成设计思想(基于XML界面管理技术) 中间层架构设计 业务…

Vue+ElementUI项目打包部署到Ubuntu服务器中

1、修改config/index.js中的assetsPublicPath: /,修改为assetsPublicPath: ./ assetsPublicPath: ./2、在build/utils.js中增加publicPath: ../../ publicPath: ../../3、打开终端&#xff0c;在根目录下执行npm run build进行打包&#xff0c;打包成功后会生成dist npm run…

cmake练习一

需求&#xff1a; 1、利用CGAL库Boost库&#xff0c;写一个关于CGAL的程序 2、使用cmake构建 1、创建目录结构 src中有一个main.cpp&#xff0c;放的是我们的主程序代码 2、安装CGAL和Boost库 略 3、编写cmakelist.txt cmake_minimum_required(VERSION 3.1.0) project(cg…

规则推理桌游

目录 Eleusis Express 1&#xff0c;规则 2&#xff0c;出牌规则示例 3&#xff0c;中文规则 Eleusis Express 原文&#xff1a;Eleusis Express 1&#xff0c;规则 简单来说就是需要一个主持人想一个出牌规则&#xff0c;其他人通过出牌试探过程推理出这个出牌规则。 …

甘特图组件DHTMLX Gantt用例 - 如何自定义任务、月标记和网格新外观

dhtmlxGantt是用于跨浏览器和跨平台应用程序的功能齐全的Gantt图表。可满足项目管理应用程序的所有需求&#xff0c;是最完善的甘特图图表库。 本文将为大家揭示DHTMLX Gantt自定义的典型用例&#xff0c;包括自定义任务、网格的新外观等&#xff0c;来展示其功能的强大性&…

电力物联网关智能通讯管理机-安科瑞黄安南

众所周知&#xff0c;网关应用于各种行业的终端设备的数据采集与数据分析&#xff0c;然后去实现设备的监测、控制、计算&#xff0c;为系统与设备之间建立通讯联系&#xff0c;达到双向的数据通讯。 网关可以实时监测并及时发现异常数据&#xff0c;同时自身根据用户规则进行…

[AutoSar NVM] 存储服务层(Service)详解

专栏 《深入浅出AutoSAR》。全文 3400 字&#xff0c; 依AutoSAR及公开知识辛苦整理&#xff0c;禁止转载。 接上一讲 [AutoSar NVM] 存储架构 "数据是新时代的石油" -- 克莱门特•M•杜森堡&#xff08;Clement M. Dusenbury&#xff09; 我们深入了解下存储服务层 …

国际腾讯云自主拼装直播 URL教程!!!

注意事项 创建转码模板 并与播放域名进行 绑定 后&#xff0c;转码配置后的直播流&#xff0c;需将播放地址的 StreamName 拼接为 StreamName_转码模板名称&#xff0c;更多详情请参见 播放配置。 前提条件 已注册腾讯云账号&#xff0c;并开通 腾讯云直播服务。 已在 域名…

python二次开发Solidworks:方程式驱动曲线

1、渐开线 import win32com.client as win32 import pythoncomswApp win32.Dispatch(sldworks.application) swApp.Visible True Nothing win32.VARIANT(pythoncom.VT_DISPATCH, None) swModel swApp.ActiveDoc swExt swModel.Extension swSelMgr swModel.SelectionManag…