在Go语言中,文件是使用一个os.File类的对象指针表示的,也可以称这指针为文件句柄(filehandle),os.Stdin和os.Stdout也是属于这个*os.File类型的。
下面举例说明
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
inputFile, inputError := os.Open(os.Args[1])//变量指向os.Open打开的文件时生成的文件句柄
if inputError != nil {
fmt.Printf("An error occurred on opening the inputfile\n")
return
}
defer inputFile.Close()
inputReader := bufio.NewReader(inputFile)
lineCounter := 0
for {
inputString, readerError := inputReader.ReadString('\n')
//inputString, readerError := inputReader.ReadBytes('\n')
if readerError == io.EOF {
return
}
lineCounter++
fmt.Printf("%d : %s", lineCounter, inputString)
}
}
上例中的的inputFile是一个os.File的类型变量,它指向一个打开的文件描述符(文件句柄)。os.Open函数接受一个文件名作为参数,上例中使用的是os.Args[1]的命令行里的第一个参数,os.Args[0]指程序本身。 使用os.Open打开的文件为只读模式,另外一个函数OpenFile(name string, flag int, perm FileMode) (file File, err error) 指代更多的操作模式。当要打开的文件不存在或程序没有足够权限时,会报错。defer.Close()函数的作用是为了保证程序结束前,这个被打开的文件能为关闭。关键字defer具有延迟执行功能。通过bufio.NewReader(inputFile),我们获得一个带缓冲的reader。之所以转换为使用bufio包里的reader(或者writer),是因为这样我们就可以使用一些实用的高级别的字符串对象,而不是底层原始的bytes数据。接着,使用ReadString('\n')或者ReadBytes('\n')方法无限循环一行行地读文件内容,值得注意的是,无论是unix系统还是windows系统,ReadString、ReadBytes还有ReadLine都能通过'\n'识别为换行。当我们读取文件直到文件结束时,readerError !=nil (readerError==io.EOF),这个for 循环便结束了。
这里还有一些可代替的方法
1、可以以字符串(字节串)方式一次性读一个完整文件的内容,io/ioutil包里的ioutil.ReadFile() 实现这个功能,它返回一个它所读到的字节的[]byte 数组及nil,或者其它错误,类似地ioutil.WriteFile则将一个[]byte 写到一个文件里去。
两个函数的原型
func ReadFile(filename string) ([]byte, error)
func WriteFile(filename string, data []byte, perm os.FileMode) error
示例:
package main
import (
"fmt"
"io/ioutil"
"os"
)
func main() {
inputFile := os.Args[1]
outputFile := os.Args[2]
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, 0x644)
if err != nil {
panic(err.Error())
}
}
2、带缓冲的读文件,除了使用ReadString(),一些情况下我们并不是一行行地读文件或二进行文件,我们可以用bufio.Reader的Read()方法,方式如下
buf := make([]byte,1024) //
...
n,err := inputReader.Read(buf)
if(n==0){break} //n为实际所读到的byte数,当文件的字节数少于缓冲数组的长度时,会返回实际的字节数,
示例:
package main
import (
"fmt"
//"io/ioutil"
"bufio"
"os"
)
func main() {
inputFile, inputError := os.Open(os.Args[1])
if inputError != nil {
fmt.Fprintf(os.Stderr, "File Error: %s\n", inputError)
}
fileReader := bufio.NewReader(inputFile)
counter := 0
for {
buf := make([]byte, 1024)
n, _ := fileReader.Read(buf)
counter++
//fmt.Printf("%d,%s", n, string(buf))
if n == 0 {
break
}
//fmt.Println(n, buf)
fmt.Printf("%d,%s", n, string(buf))
fmt.Printf("/////////////////\n")
}
fmt.Println(counter)
}
3 、从文件中读列数据。如果文件是以空格分隔的列数据,则可以使用fmt包里的Fscan系列函数,下面的例子使用了这种方式,它将从列中的读到的数据赋值到变量v1,v2和V3,然后追加到一个数组切片去。
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.Open(os.Args[1])
if err != nil {
panic(err)
}
var col1, col2, col3 []string
for {
var v1, v2, v3 string
_, err := fmt.Fscanln(file, &v1, &v2, &v3)//Fscanln 将一次只读一行,并将每列的数据赋值给相应的变量
if err != nil {
break
}
col1 = append(col1, v1)
col2 = append(col2, v2)
col3 = append(col3, v3)
}
fmt.Println(col1)
fmt.Println(col2)
fmt.Println(col3)
}
要读的文件内容为
a1 a2 a3
b1 b2 b3
c1 c2 c3
输出为
[a1 b1 c1]
[a2 b2 c2]
[a3 b3 c3]
备注: path包下有个子包filepath,它提供了些跨平台的处理文件路径及文件名的函数,例如filepath.Base(path)返回文件路径的最后一个元素。
import "path/filepath"
filename := filepath.Base(path)
有疑问加站长微信联系(非本文作者)