Go语言之文件操作,读写文件,其他文件操作

news2024/11/30 18:51:16

编码

ASCII表
众所周知,计算机起源于美国,英文只有26个字符,算上其他所有特殊符号也不会超过128个。字节是计算机的基本储存单位,一个字节(bytes)包括八个比特位(bit),能够表示出256个二进制数字,所以美国人在这里只是用到了一个字节的前七位即127个数字来对应了127个具体字符,而这张对应表就是ASCII码字符编码表,简称ASCII表。后来为了能够让计算机识别拉丁文,就将一个字节的最高位也应用了,这样就多扩展出128个二进制数字来对应新的符号。这张对应表因为是在ASCII表的基础上扩展的最高位,因此称为扩展ASCII表。到此位置,一个字节能表示的256个二进制数字都有了特殊的符号对应。

在这里插入图片描述

GBK编码
但是,当计算机发展到东亚国家后,问题又出现了,像中文,韩文,日文等符号也需要在计算机上显示。可是一个字节已经被西方国家占满了。于是,我中华民族自己重写一张对应表,直接生猛地将扩展的第八位对应拉丁文全部删掉,规定一个小于127的字符的意义与原来相同,即支持ASCII码表,但两个大于127的字符连在一起时,就表示一个汉字,这样就可以将几千个汉字对应一个个二进制数了。而这种编码方式就是GB2312,也称为中文扩展ASCII码表。再后来,我们为了对应更多的汉字规定只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的内容。这样能多出几万个二进制数字,就算甲骨文也能够用了。而这次扩展的编码方式称为GBK标准。当然,GBK标准下,一个像”苑”这样的中文符号,必须占两个字节才能存储显示。

Unicode与utf8编码
与此同时,其它国家也都开发出一套编码方式,即本国文字符号和二进制数字的对应表。而国家彼此间的编码方式是互不支持的,这会导致很多问题。于是ISO国际化标准组织为了统一编码,统计了世界上所有国家的字符,开发出了一张万国码字符表,用两个字节即六万多个二进制数字来对应。这就是Unicode编码方式。这样,每个国家都使用这套编码方式就再也不会有计算机的编码问题了。Unicode的编码特点是对于任意一个字符,都需要两个字节来存储。这对于美国人而言无异于吃上了世界的大锅饭,也就是说,如果用ASCII码表,明明一个字节就可以存储的字符现在为了兼容其他语言而需要两个字节了,比如字母I,本可以用01001001来存储,现在要用Unicode只能是00000000 01001001存储,而这将导致大量的空间被浪费掉。基于此,美国人创建了utf8编码,而utf8编码是一种针对Unicode的可变长字符编码方式,根据具体不同的字符计算出需要的字节,对于ASCII码范围的字符,就用一个字节,而且符号与数字的对应也是一致的,所以说utf8是兼容ASCII码表的。但是对于中文,一般是用三个字节存储的。

Go的字符与字节

byte就是字节的意思,一个字节就是8个二进制位。uint8,无符号整形,占8位,正好也是2的8次方。所以byte和 uint8 类型本质上没有区别,它表示的是 ACSII 表中的一个字符。


     // byte类型
    var b1 byte
    b1 = 'A'  // 必须是单引号
    // b1 = 98  // 必须是单引号
    fmt.Println(reflect.TypeOf(b1)) // 65  uint8
    fmt.Printf("%c\n",b1)
    fmt.Printf("%d\n",b1)  // ASCII数字
    fmt.Println(b1)  // ASCII数字

    // uint8类型
    var b2 uint8
    b2 = 65
    // b2 = 'c'
    fmt.Printf("%c\n",b2)
    fmt.Printf("%d\n",b2)
    fmt.Println(b2) // ASCII数字

    // var b3 byte
    var b3 rune
    b3 = '苑'
    // rune,占用4个字节,共32位比特位,所以它和 int32 本质上也没有区别。它表示的是一个 Unicode字符
    fmt.Println(b3,string(b3),reflect.TypeOf(b3))

字符串

go语⾔的string是⼀种数据类型,这个数据类型占⽤16字节空间,前8字节是⼀个指针,指向字符串值的地址,后⼋个字节是⼀个整数,标识字 符串的长度;

(1)字符串的存储原理

在这里插入图片描述
string 数据结构:源码包src/runtime/string.go:stringStruct定义了string的数据结构:

type stringStruct struct {
    str unsafe.Pointer
    len int
}

其数据结构很简单:
stringStruct.str:字符串的首地址;
stringStruct.len:字符串的长度;

string数据结构跟切片有些类似,只不过切片还有一个表示容量的成员,事实上string和切片,准确的说是byte切片经常发生转换。这个后面再详细介绍。

s1 := "hello"
s2 := s1[:]
s3 := s1[1:]
fmt.Println(&s1, (*reflect.StringHeader)(unsafe.Pointer(&s1)))
fmt.Println(&s2, (*reflect.StringHeader)(unsafe.Pointer(&s2)))
fmt.Println(&s3, (*reflect.StringHeader)(unsafe.Pointer(&s3)))

字符串类型表示字符串值的集合。字符串值是一个字节序列(可能为空)。字符串是不可变的:一旦创建,就不可能改变字符串的内容。预先声明的字符串类型是string。

字符串s的长度(以字节为单位的大小)可以使用内置函数len来发现。如果字符串是常量,则长度为编译时常量。字符串的字节可以通过索引0到len(s)-1的整数来访问。取这样一个元素的地址是非法的;如果s[i]是字符串的第i个字节,&s[i]是无效的。

go语⾔指针和C/C++指针的唯⼀差别就是:go语⾔不允许对指针做算术运算(+、-、++、–)。

但是,Go提供了⼀套底层库reflect和unsafe,它们可以把任意⼀个go指针转成uintptr类型的值,然后再像C/C++⼀样对指针做算术运算,最后再还原成go类型。所以从这个⾓度上看,go指针也是可以和C/C++指针⼀样使⽤的,只是会⽐较绕,这同时也要求使⽤者⾃⼰明⽩,如果真要把指针这么⽤,那么请记得后果⾃负。

(2)字符串的使用

   // 本质上,unicode是一个编码集,和ascii码相同,而utf8是编码规则
    var a = '苑'
    fmt.Printf("字符'苑'unicode的十进制:%d\n", a)
    fmt.Printf("字符'苑'unicode的十六进制:%x\n", a)
    fmt.Printf("字符'苑'unicode的二进制:%b\n", a)
    var b = 0b111010001000101110010001
    fmt.Printf("字符'苑'的utf8:%x\n", b)

    var c = "苑abc"
    fmt.Println(c) // 苑abc

    for i := 0; i < len(c); i++ {
        fmt.Printf("%d\n", c[i]) // 存储的字节的十进制数
    }

    for _, v := range c {
        fmt.Printf("%d,%c\n", v, v) // 通过存储的utf8解析到unicode值和对应的符号
    }

UTF-8的编码规则:
(1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。 (2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。
举例说明:
已知’苑’的unicode是82d1(1000001011010001),‘苑’的UTF-8编码需要三个字节,即格式是“1110xxxx 10xxxxxx 10xxxxxx”。然后,从’苑’的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,‘苑’的UTF-8编码是 “111010001 00010111 0010001”,转换成十六进制就是e88b91。

(3)字符串与字节串的转换

字节数组,就是一个数组,里面每一个元素都是字符,字符又跟字节划等号。所以字符串和字节数组之间可以相互转化。

// (1) 字符串类型(string) 转为字节串类型([]byte)
var s = "苑昊"
fmt.Println(s,reflect.TypeOf(s)) // 苑昊 string

var b = []byte(s)  // 默认用uft-8进行编码
fmt.Println(b,reflect.TypeOf(b)) // [232 139 145 230 152 138] []uint8

// 可以通过代码 len([]rune(s)) 来获得字符串中字符的数量, 但使用 utf8.RuneCountInString(s) 效率会更高一点.
s := "Hello,世界"
r1 := []byte(s)
r2 := []rune(s)
fmt.Println(r1) // 输出:[72 101 108 108 111 44 32 228 184 150 231 149 140]
fmt.Println(r2) // 输出:[72 101 108 108 111 44 32 19990 30028]
// 统计字节个数
fmt.Println(len(r1))
// 统计字符个数
fmt.Println(len(r2))
fmt.Println(utf8.RuneCountInString(s))

// (2) byte转为string
fmt.Println(string(b))
var data = []byte{121,117,97,110}
fmt.Println(string(data)) // yuan

这里的转化不是将string结构体中指向的byte切片直接做赋值操作,而是通过copy实现的,在数据量比较大时,这里的转化会比较耗费内存空间。

(4)练习

将字符串 “hello” 转换为 “cello”

s := "hello"
c := []byte(s)
c[0] = 'c'
s2 := string(c) //s2 == "cello"

将字符串 “hello” 反转

func reverseString(s []byte) []byte {
    var i, j = 0, len(s) - 1
    for i < j {
        s[i], s[j] = s[j], s[i]
        i++
        j--
    }
    return s
}

读写文件

.1、打开文件

os.Open()函数能够打开一个文件,返回一个*File和一个err。

//打开文件
file, err := os.Open("./满江红")
if err != nil {
    fmt.Println("err: ", err)
}
//关闭文件句柄
defer file.Close()

2、读文件

package main

import (
    "bufio"
    "fmt"
    "io"
    "io/ioutil"
    "os"
)

func readBytes(file *os.File) {
    var b = make([]byte, 3)
    n, err := file.Read(b)
    if err != nil {
        fmt.Println("err:", err)
        return
    }
    fmt.Printf("读取字节数:%d\n", n)
    fmt.Printf("切片值:%v\n", b)
    fmt.Printf("读取内容:%v\n", string(b[:n]))

}

func readLines(file *os.File) {
    reader := bufio.NewReader(file)
    for {

        // (1) 按行都字符串
        strs, err := reader.ReadString('\n') // 读取到换行符为止,读取内容包括换行符
        fmt.Print(err, strs)

        // (2) 按行都字节串
        // bytes, err := reader.ReadBytes('\n')
        // fmt.Print(bytes)
        // fmt.Print(string(bytes))
        if err == io.EOF { //io.EOF 读取到了文件的末尾
            // fmt.Println("读取到文件末尾!")
            break
        }

    }
}

func readFile() {
    content, err := ioutil.ReadFile("满江红") //包含了打开文件和读取整个文件,适用于较小文件
    if err != nil {
        fmt.Println("read file failed, err:", err)
        return
    }
    fmt.Print(string(content))
}


func main() {

    //打开文件
    file, err := os.Open("满江红") // 相对路径或者绝对路径
    if err != nil {
        fmt.Println("err: ", err)
    }
    //关闭文件句柄
    defer file.Close()

    // (1) 按字节读取数据
    // readBytes(file)
    // (2) 按行读取文件
    // readLines(file)
    // (3) 读取整个文件
    // readFile()

}

3、写文件

OpenFile是一个更一般性的文件打开函数,大多数调用者都应用Open或Create代替本函数。它会使用指定的选项(如O_RDONLY等)、指定的模式(如0666等)打开指定名称的文件。如果操作成功,返回的文件对象可用于I/O。如果出错,错误底层类型是*PathError。

func OpenFile(name string, flag int, perm FileMode) (file *File, err error) // ⽂件路径、打开模式、⽂件权限

/*
                os.O_RDONLY: 只读模式(read-only)
                os.O_WRONLY: 只写模式(write-only)
                os.O_RDWR : 读写模式(read-write)
                os.O_APPEND: 追加模式(append)
                os.O_CREATE: ⽂件不存在就创建(create a new file if none exists.)
                os.O_TRUNC: 打开并清空⽂件(必须有写权限)
                os.O_EXCL: 如与 O_CREATE ⼀起⽤,构成⼀个新建⽂件的功能,它要求⽂件必须不存在(used with O_CREATE, file must not exist)
                os.O_SYNC:同步⽅式打开,即不使⽤缓存,直接写⼊硬盘
    */

(1)只写模式

package main

import (
    "bufio"
    "fmt"
    "io/ioutil"
    "os"
)

func writeBytesOrStr(file *os.File) {
    str := "满江红666\n"
    //写入字节切片数据
    file.Write([]byte(str))
    //直接写入字符串数据
    file.WriteString("怒发冲冠,凭栏处、潇潇雨歇。")
}

func writeByBufio(file *os.File) {
    writer := bufio.NewWriter(file)
    //将数据先写入缓存,并不会到文件中
    writer.WriteString("大浪淘沙\n")
    // 必须flush将缓存中的内容写入文件
    // writer.Flush()
}

func writeFile() {
    str := "怒发冲冠,凭栏处、潇潇雨歇。"
    err := ioutil.WriteFile("满江红", []byte(str), 0666)
    if err != nil {
        fmt.Println("write file failed, err:", err)
        return
    }
}

func main() {

    file, err := os.OpenFile("满江红.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
    if err != nil {
        fmt.Println("open file failed, err:", err)
        return
    }
    defer file.Close()

    // 写字节或者字符串
    writeBytesOrStr(file)
    // flush写
    writeByBufio(file)
    // 写文件
    writeFile()

}

0777:-rwxrwxrwx,创建了一个普通文件,所有人拥有所有的读、写、执行权限 0666:-rw-rw-rw-,创建了一个普通文件,所有人拥有对该文件的读、写权限,但是都不可执行 0644:-rw-r–r–,创建了一个普通文件,文件所有者对该文件有读写权限,用户组和其他人只有读权限,没有执行权限

(2)读写模式

读取一个文件每一行内容,并追加一行该行的字符个数

package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
    "strings"
)

func main() {

    file, err := os.OpenFile("读写满江红", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
    if err != nil {
        fmt.Println("open file failed, err:", err)
        return
    }
    defer file.Close()

    reader := bufio.NewReader(file)
    writer := bufio.NewWriter(file)

    for true {
        // (1) 按行都字符串
        strs, err := reader.ReadString('\n') // 读取到换行符为止,读取内容包括换行符
        content := strings.Trim(strs, "\n")
        s := fmt.Sprintf("\n该行长度为%d,内容为:%s", len([]rune(content)), content)

        // (2) 将行数记录追加进入文件
        writer.WriteString(s)
        writer.Flush()

        if err == io.EOF {
            break
        }

    }

}

其它文件操作

(1) 删除文件

os.Remove(fname)

(2) 创建目录

dname :=“rain”

os.Mkdir(dname,os.ModeDir|os.ModePerm)

(3)获取文件信息

通过os.Stat方法,我们可以获取文件的信息,比如文件大小、名字等。

func main() {
    f,err:=os.Stat("满江红")
    if err ==nil {
        fmt.Println("name:",f.Name())
        fmt.Println("size:",f.Size())
        fmt.Println("is dir:",f.IsDir())
        fmt.Println("mode::",f.Mode())
        fmt.Println("modTime:",f.ModTime())
    }
}

以上就是可以获取到的文件信息,还包括判断是否是目录,权限模式和修改时间。所以我们对于文件的信息获取要使用os.Stat函数,它可以在不打开文件的情况下,高效获取文件信息。
os.Stat函数有两个返回值,一个是文件信息,一个是err,通过err我们可以判断文件是否存在。首先,err==nil的时候,文件肯定是存在的;其次err!=nil的时候也不代表不存在,通过err是否等于os.IsNotExist来判断一个文件不存在。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大语言模型:从应用到产出;百度网盘推出AI的图搜功能

&#x1f989; AI新闻 &#x1f680; 百度网盘推出基于AI的高级图搜功能&#xff0c;提供更精确、全面的搜索结果 摘要&#xff1a;百度网盘近日推出了一项名为“高级图搜”的AI功能。通过基于向量的语义搜索&#xff0c;该功能可以理解时间、地点、人物、事件等组合搜索语句…

LeetCode 热题 100(二):滑动窗口。3. 无重复字符的最长子串、 438.找到字符串中所有字母异位词

滑动窗口例题&#xff1a; 一、3. 无重复字符的最长子串 题目要求&#xff1a;就是说找到包含不同字母最长的串。 思路&#xff1a;可以想到使用set去重&#xff0c;同时应用滑动窗口。本质上还是双指针法&#xff0c;l是窗口左边界 r是窗口右边界。 如果set包含了当前字母就…

Can总线概述

1&#xff1a;Can总线简介 CAN总线是控制器局域网络&#xff08;Controller Area Network&#xff0c;CAN&#xff09;的简称&#xff0c;由德国BOSCH公司开发&#xff0c;并最终成为国际标准&#xff08;ISO 11898-1&#xff09;&#xff0c;是一种串行数据通信总线。 在当前…

webpack-theme-color-replacer+elementui自定义配置主题色

webpack-theme-color-replacer原理是通过获取到配置数组里的颜色值&#xff0c;在触发换色方法时&#xff0c;elementui使用的颜色值存在与配置表中颜色一致的颜色&#xff0c;则改颜色会被替换成新的颜色值。 若是自定义的css文件&#xff0c;需要配置css文件路径 若是需要修…

视频弹题、视频答题来实现视频防录屏和防挂机

视频弹题、视频答题来实现视频防录屏和防挂机 1.视频播放到某个时间点&#xff0c;弹出问题卡&#xff0c;学员只有正确回答课件视频中弹出的问题之后才能继续观看视频。 2.通过互动问答的方式&#xff0c;不仅有利于巩固前边学习的知识点&#xff0c;评估学员的学习效果&#…

ShardingSphere分库分表实战之水平分库和水平分表

&#x1f680; ShardingSphere &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&…

边框动画 单边追随

时间短就直接看第三个使用 clip-path animation 完成。 <!DOCTYPE html> <html><head><style>/* 第一个 */.btn {width: 100px;height: 40px;background: yellow;position: relative;display: flex;align-items: center;justify-content: center;bor…

Unity游戏源码分享-单车骑行游戏

Unity游戏源码分享-单车骑行游戏 项目地址&#xff1a;https://download.csdn.net/download/Highning0007/88057717

【问题总结】Docker环境下备份和恢复postgresql数据库

目录 文章目录 以从备份恢复forest_resources库为例一、备份数据库二、需要还原的数据库准备1 删除掉远程的库。2 重新创建一个空的库。可以使用sql3 找到数据库存放的路径&#xff0c;并将备份文件上传到对应的路径下 三、 进入docker容器内部&#xff0c;执行数据库恢复附录…

C++拷贝构造函数原理解析

喵~ 一、构造函数1.1 默认构造函数1.2 自定义的默认构造函数1.3 自定义带参数的构造函数 二、拷贝构造函数的基本使用2.1 浅拷贝和深拷贝&#xff08;原理及区别&#xff09; 一、构造函数 在C面向对象的学习中&#xff0c;对于构造函数应该并不陌生&#xff0c;有默认的构造函…

第47节:cesium 热力图效果(含源码+视频)

结果示例: 完整源码: index.html中增加<script src="./static/lib/heatmap.js"></script> heatmap.js /** heatmap.js v2.0.5 | JavaScript Heatmap Library** Copyright 2008-2016 Patrick Wied <heatmapjs@patrick-wied.at> - All rights re…

了解 3DS MAX 3D摄像机跟踪设置:第 7 部分

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. 在SynthEyes中跟踪素材 步骤 1 打开SynthEyes软件。 打开合成之眼 步骤 2 在跟踪素材之前&#xff0c;您需要设置首选项。因为&#xff0c;你 稍后将在 3ds Max 中工作&#xff0c;必须根据 3ds Max…

双电源并用问题与解决方案

双电源并用问题 曾经有客户在电源模块应用过程中出现过这样的应用场景&#xff0c;如下图1所示。客户使用两路电源给后端电路进行供电&#xff0c;要求在不断电的情况下切换输入电源&#xff0c;此过程中发现后端电路会出现损坏。对各个节点波形进行分析后发现&#xff0c;在给…

在VMware上安装Linux虚拟机

一&#xff0c;下载CentOS操作系统 下载地址&#xff1a;https://vault.centos.org/7.6.1810/isos/x86_64/ 打开下载网址&#xff0c;找到centos-7-x86_64-DVD-1810.iso&#xff0c;下载即可。 下载的漫长等待。。。。。 二、创建虚拟机 打开下载好的VMware。 点击创建新的虚拟…

SPI 的初识

SPI 介绍 Q: SPI 是什么&#xff1f; A: SPI是串行外设接口&#xff08;Serial Peripheral Interface&#xff09;的缩写&#xff0c;是一种高速的&#xff08;比IIC快&#xff09;&#xff0c;全双工&#xff08;IIC是半双工&#xff09;&#xff0c;同步的通信总线&#xf…

【AUTOSAR】:车载以太网

车载以太网 References参考文献车载以太网的物理连接MACPHYPHY的主从关系100BASE-T1回音消除车载以太网的应用层协议References参考文献 汽车软件通信中间件SOME/IP简述100BASE-T1以太网:汽车网络的发展车载以太网的物理连接 MAC MAC(Media Access Control介质访问)一般集成…

数字贸易时代如何高效管理水果出口外贸业务

目前中国水果出口的国家有很多&#xff0c;其中包括但不限于以下几个国家&#xff1a;美国、日本、韩国、俄罗斯、德国、法国、英国、荷兰、澳大利亚、新加坡、马来西亚、泰国、印度尼西亚、巴西、阿联酋等。2023年前5个月&#xff0c;出口目的国家/地区排名前十的分别为&#…

【网络技术】计算机网络介绍

写在前面 计算机网络是指将多台计算机连接起来&#xff0c;使它们能够相互通信和共享资源的系统。 它是现代计算机科学中的重要分支之一&#xff0c;为全球范围内的信息交流和数据传输提供了基础。 本文将介绍计算机网络的基础概念、体系结构、协议、常见问题等的知识。 一、基…

uni-app:scroll-view滚动盒子,实现横(纵)向滚动条

参照&#xff1a;scroll-view | uni-app官网 (dcloud.net.cn) 样式&#xff1a; 代码&#xff1a; <template><view class"box"><scroll-view scroll-x"true" class"scroll"><view class"box1"> <view c…

ElasticSearch搜索相关性及打分的相关原理

文章目录 一、相关性和打分简介二、TF-IDF得分计算公式三、BM25&#xff08;Best Matching 25&#xff09;四、使用explain查看TF-IDF五、通过Boosting控制相关度 一、相关性和打分简介 举个例子来说明&#xff1a; 假设有一个电商网站&#xff0c;用户在搜索框中输入了关键词&…