GoLang读写数据---上

  • 读取用户的输入
    • scanf
    • scan和scanln
    • Fscan系列和Sscanf系列
    • bufio
  • 文件读写
    • 读文件
      • 其他类似函数:
    • compress包:读取压缩文件
    • 写文件
  • 实例演示

读取用户的输入

我们如何读取用户的键盘(控制台)输入呢?

从键盘和标准输入 os.Stdin 读取输入,最简单的办法是使用 fmt 包提供的相关函数。

scanf

  • scanf:按照给定的格式依次读取数据(包括非法数据),不能换行输入(如果要换行需要在前面加一个scanln吸收掉回车符,就像c语言中的getchar)
package mainimport "fmt"//假如我们要输入一个人的年龄和名字
func main() {var name stringvar age int8fmt.Scanf("%s", &name)fmt.Scanf("%d", &age)//fmt.Scanf("%s %d", &name, &age)  对于scanf,这句话等价于上面两句话fmt.Println(name, "  ", age)
}

这两种写法都一样,如果我要把名字和年龄分两行输入是不行的,运行结果就像这样:

第一种输入方法(一次性输入):


第二种输入方法(把名字和年龄分两次输入):


scanf有两个返回值n和err,n是按指定格式成功输入数据的个数,err是读取过程中遇到的错误,如果没有错误,err的值就为nil

package mainimport "fmt"//假如我们要输入一个人的年龄和名字
func main()  {var name stringvar age int8n, err := fmt.Scanf("%s %d", &name, &age)  //用n,err分别接受scanf扫描成功的个数和错误返回值if err == nil{          //如果没有错误就输出name和age的值fmt.Println(name, "  ", age)}else{fmt.Println("读取成功",n, "个","错误:",err)}}

我们用刚刚第二种没有成功的输入方法试试,看是什么错误


可以看到,我们只成功输入了bob这一个数据,有一个错误叫unexpected newline,这个错误其实就是我们输入的回车,因为scanf函数遇到换行符就结束,从缓冲区依次读取以空格分开的数据;对我们这个程序而言,首先按%s读入了bob,然后再按%d读取下一个数据(回车),但是回车键不是十进制整形数据,它按%d怎么可能读得进去呢,所以就出现了只成功读取一个数据,报错为 “没有意料到的新行”


scan和scanln

  • scan:比scanf高级,依次读取数据,遇到回车会忽略,可以换行输入(如果要先用了scan输入,再用scanf输入的话,需要在中间加一个scanln)
  • scanln:类似scan,但是遇到换行(回车)立马结束输入,如果要换行输入必须用多个scanln

跟scanf差不多,都是有两个返回值,一个是读取成功个数,另一个是错误值

package mainimport "fmt"//假如我们要输入一个人的年龄和名字
func main()  {var name stringvar age int8n, err := fmt.Scan(&name, &age)  //用n,err分别接受scanf扫描成功的个数和错误返回值/*n, err := fmt.Scan(&name)n, err = fmt.Scan(&age)*/    //对于scan这种写法也等价于上面那种写法if err == nil{          //如果没有错误就输出name和age的值fmt.Println("输出:", name, "  ", age)}else{fmt.Println("读取成功",n, "个","错误:",err)}
}

对于scan,我们是可以多个数据多行输入的

对于scanln,又有些不同了

package mainimport "fmt"//假如我们要输入一个人的年龄和名字
func main()  {var name stringvar age int8n, err := fmt.Scanln(&name, &age)  //这种写法的话必须把name和age一行输入,因为scanln是以回车为标志结束n, err := fmt.Scanln(&name) //这样写就可以分两行输入name和agen, err  = fmt.Scanln(&age)if err == nil{          //如果没有错误就输出name和age的值fmt.Println("输出:", name, "  ", age)}else{fmt.Println("读取成功",n, "个","错误:",err)}
}

其实scanln再换行的时候会把缓冲区的回车也收走,但是scan和scanf不会,所以就导致了scanf不能分多行输入数据。但是scan却可以,它虽然没有收走缓冲区的回车符,但是不会把回车符读进去,遇到回车它会继续读取下一个数据,而scanf会按照我们给的格式(如%d去读取数据),但是肯定读不进去的,所以就读取失败了


举例:

package mainimport "fmt"//假如我们要输入一个人的年龄和名字
func main()  {var name stringvar age int8fmt.Scan(&name)// 把Scan换成Scanln就可以了n, err  := fmt.Scanf("%d", &age)//原因:scan没有把第一行输入结束后的回车收走,导致scanf按%d的格式去读取回车符,那肯定读取失败啊//而scanln会把第一行输入结束的回车符读走,scanf会按%的格式去读取我们后面输入的数据if err == nil{         fmt.Println("输出:", name, "  ", age)}else{fmt.Println("读取成功",n, "个","错误:",err)}
}

正确写法:

package mainimport "fmt"//假如我们要输入一个人的年龄和名字
func main()  {var name stringvar age int8fmt.Scan(&name)fmt.Scanln()fmt.Scanf("%d", &age)}

Fscan系列和Sscanf系列

  • Sscanf : 从字符串str扫描文本,根据format 参数指定的格式将成功读取的空白分隔的值保存进成功传递给本函数的参数。返回成功扫描的条目个数和遇到的任何错误。
package mainimport "fmt"var (input  = "大忽悠 / 18 / 大傻子"format = "%s / %d / %s"name   stringage    intnick   string
)func main() {fmt.Sscanf(input, format, &name, &age, &nick)fmt.Printf("姓名: %s ,年龄: %d, 绰号: %s", name, age, nick)
}


扩展:

  • Fscan系列
func Fscan(r io.Reader, a ...interface{}) (n int, err error)
func Fscanln(r io.Reader, a ...interface{}) (n int, err error)
func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error)

这几个函数功能分别类似于fmt.Scan、fmt.Scanf、fmt.Scanln三个函数,只不过它们不是从标准输入中读取数据而是从io.Reader中读取数据。

  • Sscan系列
func Sscan(str string, a ...interface{}) (n int, err error)
func Sscanln(str string, a ...interface{}) (n int, err error)
func Sscanf(str string, format string, a ...interface{}) (n int, err error)

这几个函数功能分别类似于fmt.Scan、fmt.Scanf、fmt.Scanln三个函数,只不过它们不是从标准输入中读取数据而是从指定字符串中读取数据。


bufio

您也可以使用 bufio 包提供的缓冲读取(buffered reader)来读取数据,正如以下例子所示:

package mainimport ("bufio""fmt""os"
)var inputReader *bufio.Reader
var input string
var err errorfunc main() {inputReader = bufio.NewReader(os.Stdin)fmt.Println("请输入数据,换行结束本次输入: ")input, err = inputReader.ReadString('\n')if err == nil {fmt.Printf("The input was: %s\n", input)}
}


inputReader 是一个指向 bufio.Reader 的指针。inputReader := bufio.NewReader(os.Stdin) 这行代码,将会创建一个读取器,并将其与标准输入绑定。

bufio.NewReader() 构造函数的签名为:func NewReader(rd io.Reader) *Reader

该函数的实参可以是满足 io.Reader 接口的任意对象,函数返回一个新的带缓冲的 io.Reader 对象,它将从指定读取器(例如 os.Stdin)读取内容。

返回的读取器对象提供一个方法 ReadString(delim byte),该方法从输入中读取内容,直到碰到 delim 指定的字符,然后将读取到的内容连同 delim 字符一起放到缓冲区。

ReadString 返回读取到的字符串,如果碰到错误则返回 nil。如果它一直读到文件结束,则返回读取到的字符串和 io.EOF。如果读取过程中没有碰到 delim 字符,将返回错误 err != nil。

在上面的例子中,我们会读取键盘输入,直到回车键(\n)被按下。

屏幕是标准输出 os.Stdout;os.Stderr 用于显示错误信息,大多数情况下等同于 os.Stdout。

一般情况下,我们会省略变量声明,而使用 :=,例如:

inputReader := bufio.NewReader(os.Stdin)
input, err := inputReader.ReadString('\n')

文件读写

读文件

在 Go 语言中,文件使用指向 os.File 类型的指针来表示的,也叫做文件句柄。我们在前面章节使用到过标准输入 os.Stdin 和标准输出 os.Stdout,他们的类型都是 *os.File。让我们来看看下面这个程序:

package mainimport ("bufio""fmt""io""os"
)func main() {inputFile, inputError := os.Open("dhy.test")if inputError != nil {fmt.Printf("An error occurred on opening the inputfile\n" +"Does the file exist?\n" +"Have you got acces to it?\n")return // exit the function on error}defer inputFile.Close()inputReader := bufio.NewReader(inputFile)for {inputString, readerError := inputReader.ReadString('\n')fmt.Printf("The input was: %s", inputString)if readerError == io.EOF {return}}
}

变量 inputFile 是 *os.File 类型的。该类型是一个结构,表示一个打开文件的描述符(文件句柄)。然后,使用 os 包里的 Open 函数来打开一个文件。该函数的参数是文件名,类型为 string。在上面的程序中,我们以只读模式打开 input.dat 文件。

如果文件不存在或者程序没有足够的权限打开这个文件,Open函数会返回一个错误:inputFile, inputError = os.Open(“input.dat”)。如果文件打开正常,我们就使用 defer inputFile.Close() 语句确保在程序退出前关闭该文件。然后,我们使用 bufio.NewReader 来获得一个读取器变量。

通过使用 bufio 包提供的读取器(写入器也类似),如上面程序所示,我们可以很方便的操作相对高层的 string 对象,而避免了去操作比较底层的字节。

接着,我们在一个无限循环中使用 ReadString(‘\n’) 或 ReadBytes(‘\n’) 将文件的内容逐行(行结束符 ‘\n’)读取出来。

注意: 在之前的例子中,我们看到,Unix和Linux的行结束符是 \n,而Windows的行结束符是 \r\n。在使用 ReadString 和 ReadBytes 方法的时候,我们不需要关心操作系统的类型,直接使用 \n 就可以了。另外,我们也可以使用 ReadLine() 方法来实现相同的功能。

一旦读取到文件末尾,变量 readerError 的值将变成非空(事实上,其值为常量 io.EOF),我们就会执行 return 语句从而退出循环。


其他类似函数:

1) 将整个文件的内容读到一个字符串里:

如果您想这么做,可以使用 io/ioutil 包里的 ioutil.ReadFile() 方法,该方法第一个返回值的类型是 []byte,里面存放读取到的内容,第二个返回值是错误,如果没有错误发生,第二个返回值为 nil。

类似的,函数 WriteFile() 可以将 []byte 的值写入文件。

package mainimport ("fmt""io/ioutil""os"
)func main() {inputFile := "main.go"outputFile := "products_copy.txt"buf, err := ioutil.ReadFile(inputFile)if err != nil {fmt.Fprintf(os.Stderr, "File Error: %s\n", err)}fmt.Printf("%s\n", string(buf))err = ioutil.WriteFile(outputFile, buf, 0644)if err != nil {panic(err.Error())}
}

2) 带缓冲的读取

在很多情况下,文件的内容是不按行划分的,或者干脆就是一个二进制文件。在这种情况下,ReadString()就无法使用了,我们可以使用 bufio.Reader 的 Read(),它只接收一个参数:

package mainimport ("bufio""fmt""os"
)func main() {file, err := os.Open("main.go")if err != nil {fmt.Printf("文件打开出错: %v", err)return}reader := bufio.NewReader(file)bytes := make([]byte, 1024)reader.Read(bytes)fmt.Println(string(bytes))
}

变量 n 的值表示读取到的字节数.


3) 按列读取文件中的数据

如果数据是按列排列并用空格分隔的,你可以使用 fmt 包提供的以 FScan 开头的一系列函数来读取他们。请看以下程序,我们将 3 列的数据分别读入变量 v1、v2 和 v3 内,然后分别把他们添加到切片的尾部。

package mainimport ("fmt""os"
)func main() {file, err := os.Open("dhy.test")if err != nil {panic(err)}defer file.Close()var col1, col2, col3 []stringfor {var v1, v2, v3 string_, err := fmt.Fscanln(file, &v1, &v2, &v3)// scans until newlineif err != nil {break}col1 = append(col1, v1)col2 = append(col2, v2)col3 = append(col3, v3)}fmt.Println(col1)fmt.Println(col2)fmt.Println(col3)
}


注意: path 包里包含一个子包叫 filepath,这个子包提供了跨平台的函数,用于处理文件名和路径。例如 Base() 函数用于获得路径中的最后一个元素(不包含后面的分隔符):

import "path/filepath"
filename := filepath.Base(path)

compress包:读取压缩文件

compress包提供了读取压缩文件的功能,支持的压缩文件格式为:bzip2、flate、gzip、lzw 和 zlib。

下面的程序展示了如何读取一个 gzip 文件。

package main
import ("fmt""bufio""os""compress/gzip"
)
func main() {fName := "MyFile.gz"var r *bufio.Readerfi, err := os.Open(fName)if err != nil {fmt.Fprintf(os.Stderr, "%v, Can't open %s: error: %s\n", os.Args[0], fName,err)os.Exit(1)}defer fi.Close()fz, err := gzip.NewReader(fi)if err != nil {r = bufio.NewReader(fi)} else {r = bufio.NewReader(fz)}for {line, err := r.ReadString('\n')if err != nil {fmt.Println("Done reading file")os.Exit(0)}fmt.Println(line)}
}

Golang zip 压缩与解压


写文件

package main
import ("os""bufio""fmt"
)
func main () {// var outputWriter *bufio.Writer// var outputFile *os.File// var outputError os.Error// var outputString stringoutputFile, outputError := os.OpenFile("output.dat", os.O_WRONLY|os.O_CREATE, 0666)if outputError != nil {fmt.Printf("An error occurred with file opening or creation\n")return}defer outputFile.Close()outputWriter := bufio.NewWriter(outputFile)outputString := "hello world!\n"for i:=0; i<10; i++ {outputWriter.WriteString(outputString)}outputWriter.Flush()
}

除了文件句柄,我们还需要 bufio 的 Writer。我们以只写模式打开文件 output.dat,如果文件不存在则自动创建:

outputFile, outputError := os.OpenFile("output.dat", os.O_WRONLY|os.O_CREATE, 0666)

可以看到,OpenFile 函数有三个参数:文件名、一个或多个标志(使用逻辑运算符“|”连接),使用的文件权限。

我们通常会用到以下标志:

  • os.O_RDONLY:只读
  • os.O_WRONLY:只写
  • os.O_CREATE:创建:如果指定文件不存在,就创建该文件。
  • os.O_TRUNC:截断:如果指定文件已存在,就将该文件的长度截为0。

在读文件的时候,文件的权限是被忽略的,所以在使用 OpenFile 时传入的第三个参数可以用0。而在写文件时,不管是 Unix 还是 Windows,都需要使用 0666。

然后,我们创建一个写入器(缓冲区)对象:

outputWriter := bufio.NewWriter(outputFile)

接着,使用一个 for 循环,将字符串写入缓冲区,写 10 次:outputWriter.WriteString(outputString)

缓冲区的内容紧接着被完全写入文件:outputWriter.Flush()

如果写入的东西很简单,我们可以使用 fmt.Fprintf(outputFile, “Some test data.\n”) 直接将内容写入文件。fmt 包里的 F 开头的 Print 函数可以直接写入任何 io.Writer,包括文件。

下面展示了不使用 fmt.FPrintf 函数,使用其他函数如何写文件:

package main
import "os"
func main() {os.Stdout.WriteString("hello, world\n")f, _ := os.OpenFile("test", os.O_CREATE|os.O_WRONLY, 0666)defer f.Close()f.WriteString("hello, world in a file\n")
}

使用 os.Stdout.WriteString(“hello, world\n”),我们可以输出到屏幕。

我们以只写模式创建或打开文件”test”,并且忽略了可能发生的错误:f, _ := os.OpenFile(“test”, os.O_CREATE|os.O_WRONLY, 0666)

我们不使用缓冲区,直接将内容写入文件:f.WriteString( )


实例演示

将学生数据以JSON字符串的格式保存到文件中,然后再从文件中读取某个学生的数据:

type StuInterface interface {Save()Get(id int) Stu
}
package serviceimport ("bufio""encoding/json""fmt""os"
)type Stu struct {Name stringAge  intId   int
}func NewStu(name string, age int, id int) *Stu {return &Stu{Name: name, Age: age, Id: id}
}//Save 将学生数据序列化后保存到文件中
func (s *Stu) Save() {file, err := os.OpenFile("stu.dbl", os.O_CREATE|os.O_APPEND, 0666)if err != nil {panic(err)return}defer file.Close()//只会导出结构体中被导出的字段---属性名大写marshal, err := json.Marshal(s)if err != nil {panic(err)return}fmt.Println("序列化结果: ", string(marshal))file.Write(marshal)file.WriteString("\n")
}//Get 从文件中读取学生数据
func (s *Stu) Get(id int) Stu {file, err := os.Open("stu.dbl")if err != nil {panic(err)return Stu{}}defer file.Close()reader := bufio.NewReader(file)var jsonData [][]bytei := 0for {line, _, err := reader.ReadLine()if err != nil {break}i++jsonData = append(jsonData, line)}var stu Stufor _, stuJson := range jsonData {err := json.Unmarshal(stuJson, &stu)if err != nil {panic(err)return Stu{}}if stu.Id == id {return stu}}return Stu{}
}
package mainimport (. "GoStudy/service""fmt"
)func main() {var s StuInterface = NewStu("狗蛋儿", 18, 2)//s.Save()stu := s.Get(3)//判断是否为空结构体if stu != (Stu{}) {fmt.Println("查找的结果为: ", stu)}
}

GoLang读写数据---上相关推荐

  1. android json mysql_Android通过json向MySQL中读写数据的方法详解【读取篇】

    本文实例讲述了Android通过json向MySQL中读取数据的方法.分享给大家供大家参考,具体如下: 首先 要定义几个解析json的方法parseJsonMulti,代码如下: private vo ...

  2. Java18-day09【字节缓冲流、字符流、编码表、字符串与字符流中的编码解码问题、字符流读写数据的方式、字符缓冲流、IO流小结】

    视频+资料(工程源码.笔记)[链接:https://pan.baidu.com/s/1MdFNUADVSFf-lVw3SJRvtg   提取码:zjxs] Java基础--学习笔记(零起点打开java ...

  3. m5310模组数据上传至onenet_硬核干货!基于M5310-A的NB-IoT水表通信模块软件业务逻辑分享...

    根据不同的应用场景需求,目前NB-IoT水表主要有以下几种方案: 图1 几种常见NB水表方案 接下来将从NB-IoT水表上电开机.模组初始化.入网判断.业务逻辑四个环节来详细讲述,以下业务流程仅供参考 ...

  4. Python StringIO实现内存缓冲区中读写数据

    StringIO的行为与file对象非常像,但它不是磁盘上文件,而是一个内存里的"文件",我们可以像操作磁盘文件那样来操作StringIO.这篇文章主要介绍了Python Stri ...

  5. 测试一个config server 服务器挂机后,集群是否能读写数据

    测试架构: 测试目的: 测试一个config server 服务器挂机后,集群是否能读写数据. 测试原因: 上周因为内存吃紧,准备添加内存.在查看服务器时,把一台服务器关机检查.     关机后,WE ...

  6. Hadoop之HDFS读写数据流程

    Hadoop之HDFS读写数据流程 目录 HDFS写数据流程 HDFS读数据流程 网络拓扑概念 机架感知 1. HDFS写数据流程 HDFS写数据流程,如下图 客户端通过Distributed Fil ...

  7. ENVI IDL读写数据

    最近写程序不知道怎么写envi标准格式文件的头文件,在网上搜了半天,也没找到相关的信息.找到一个 ENVI_SETUP_HEAD函数,也不知怎么用.下面的内容可能以后用的着,先留着吧. 引用自:htt ...

  8. HDFS读写数据的原理

    目录 1 概述 2 HDFS写数据流程 3 HDFS读数据流程 目录 最近由于要准备面试,就把之前学过的东西好好整理下,权当是复习. 下面说下HDFS读写数据的原理. 1 概述 HDFS集群分为两大角 ...

  9. android ble蓝牙接收不到数据_Android蓝牙4.0 Ble读写数据详解 -2

    Android蓝牙4.0 Ble读写数据详解 -2 上一篇说了如何扫描与链接蓝牙 这篇文章讲讲与蓝牙的数据传输,与一些踩到的坑. 先介绍一款调试工具,专门调试Ble蓝牙的app.名字叫:nRF-Con ...

  10. SQL Server 如何读写数据

    01. SQL Server 如何读写数据 一. 数据读写流程简要 SQL Server作为一个关系型数据库,自然也维持了事务的ACID特性,数据库的读写冲突由事务隔离级别控制.无论有没有显示开启事务 ...

最新文章

  1. c语言中的if语句_If ... C中的其他语句解释
  2. MIME types [记录]
  3. 《高性能javascript》 领悟随笔之-------DOM编程篇(二)
  4. 编译安装mariadb-10.0.10
  5. python数据分析工资_python3对拉勾数据进行可视化分析的方法详解
  6. ECCV 2018 | OR-CNN行人检测:为‘遮挡’而生
  7. 用友3.0谋局“新两化” 融合创新迸发新动能
  8. 剑指offer之【数组中的逆序对】
  9. css --- [练手小项目]样式小结(字体、颜色的语义 清除浮动的使用)
  10. 控件的WM_NOTIFY消息映射
  11. 在前端团队的那些日子(初见)
  12. 一个服务器启动2套mysql_一个服务器启动两个mysql实例
  13. linux中rpm命令,linux中rpm命令使用介绍
  14. java并发测试工具_Java并发测试工具类
  15. 关于百度机器人搜索你网站的页面权限设置
  16. python九九乘法表如何对齐_python怎么样输出九九乘法表
  17. 护肤品html作业,聚美优品美容产品热点.html
  18. 前端项目如何使用svg矢量图
  19. mysql暴力撞库与弱密码检测
  20. 初中计算机word试题,初中计算机会考word试题WORD15

热门文章

  1. 使用ftp访问资源管理器托拽文件下载出现“当前的安全设置不允许从该位置下载文件”提示
  2. 计算机病毒查杀记录,如何看eset nod32防病毒查杀历史记录?
  3. npm ERR! Command failed: git clone
  4. Google Dapper,大规模分布式系统的跟踪系统
  5. 谁说大象不能跳舞读后感
  6. 静态网页设计——春节
  7. Ubuntu配置静态IP以及interfaces配置不生效问题解决
  8. 如何在html中做超链接,如何在HTML上做一个超链接?
  9. 使用UltraEdit编辑器之HelloWorld的实现
  10. CleanMyMac2023免费版系统清理优化工具