起因
只想读取文件的最后一行,但是只找到一些通过循环到最后一行的方法,但是需求是只需要看最后一行,这样大可不必,我的文本内容是多行json的字符串,目前测试只有两行
尝试方法
一开始想倒着读取,从文件末尾开始,然后找到上一行换行符也就是'\n' 于是就有了如下代码
func getLastline() {
file, err := os.OpenFile("checkLog.txt", os.O_RDONLY, 0600)
if err != nil {
panic(err)
}
defer file.Close()
info, _ := file.Stat()
if info.Size() > 0 {
index := int64(-1)
r := bufio.NewReader(file)
for {
index--
file.Seek(index, io.SeekEnd)
readByte, err := r.ReadByte()
if readByte == '\n' {
break
}
if err != nil {
if err == io.EOF {
break
}
fmt.Println(err) //{"cardTime":"2023-07-08 21:46:58","cardId":"tarot5.jpg"}:"tarot3.jpg"}
}
}
line, _, _ := r.ReadLine()
fmt.Println(string(line))
}
}
问题出现了最后打印出的结果是:
{"cardTime":"2023-07-08 21:46:58","cardId":"tarot5.jpg"}:"tarot3.jpg"}
但是期望值应该是:
{"cardTime":"2023-07-08 21:46:58","cardId":"tarot5.jpg"}
这多出来一节不知道是怎么回事,误打误撞又调用了一次file.Seek()后又正确了
func getLastERR() {
file, err := os.OpenFile("checkLog.txt", os.O_RDONLY, 0600)
if err != nil {
panic(err)
}
defer file.Close()
info, _ := file.Stat()
if info.Size() > 0 {
index := int64(-1)
r := bufio.NewReader(file)
for {
index--
file.Seek(index, io.SeekEnd)
readByte, err := r.ReadByte()
if readByte == '\n' {
break
}
if err != nil {
if err == io.EOF {
break
}
fmt.Println(err)
}
}
file.Seek(0, io.SeekEnd)
line, _, _ := r.ReadLine()
fmt.Println(string(line))//{"cardTime":"2023-07-08 21:46:58","cardId":"tarot5.jpg"}
}
}
打印出如下:
{"cardTime":"2023-07-08 21:46:58","cardId":"tarot5.jpg"}
当时直接匪夷所思了,猜测是因为bufio.reader是有缓存的,所以读取到最后一个字节是'\n'后依旧有一部分缓存在buf里,重新定位了一下seek就好了,那么在if readByte=='\n'后重定位seek应该也是可以的,于是代码又改成这样,结果也是正确的。
func getLastERR() {
file, err := os.OpenFile("checkLog.txt", os.O_RDONLY, 0600)
if err != nil {
panic(err)
}
defer file.Close()
info, _ := file.Stat()
if info.Size() > 0 {
index := int64(-1)
r := bufio.NewReader(file)
for {
index--
file.Seek(index, io.SeekEnd)
readByte, err := r.ReadByte()
if readByte == '\n' {
file.Seek(0, io.SeekEnd)
break
}
if err != nil {
if err == io.EOF {
break
}
fmt.Println(err)
}
}
line, _, _ := r.ReadLine()
fmt.Println(string(line))//{"cardTime":"2023-07-08 21:46:58","cardId":"tarot5.jpg"}
}
}
尝试peek看一下构造reader的时候缓冲区的内容于是r.peek了一下:
bs,_:=r.Peek(4096)
fmt.Println(string(bs))//{"cardTime":"2023-07-08 21:46:51","cardId":"tarot3.jpg"}{"cardTime":"2023-07-08 21:46:58","cardId":"tarot5.jpg"}
{"cardTime":"2023-07-08 21:46:51","cardId":"tarot3.jpg"}
{"cardTime":"2023-07-08 21:46:58","cardId":"tarot5.jpg"}
当场又匪夷所思了,peek之后即使没有重新file.Seek(),最后一行的读取又正确了。按理说peek只是看一下缓冲区里后面的数据,不应该会有操作才对,不知道为什么peek一下读取的内容就正确了。
func getLastERR() {
file, err := os.OpenFile("checkLog.txt", os.O_RDONLY, 0600)
if err != nil {
panic(err)
}
defer file.Close()
info, _ := file.Stat()
if info.Size() > 0 {
index := int64(-1)
r := bufio.NewReader(file)
bs,_:=r.Peek(4096)
fmt.Println(string(bs))//{"cardTime":"2023-07-08 21:46:51","cardId":"tarot3.jpg"}{"cardTime":"2023-07-08 21:46:58","cardId":"tarot5.jpg"}
for {
index--
file.Seek(index, io.SeekEnd)
readByte, err := r.ReadByte()
if readByte == '\n' {
//file.Seek(0, io.SeekEnd)
break
}
if err != nil {
if err == io.EOF {
break
}
fmt.Println(err)
}
}
line, _, _ := r.ReadLine()
fmt.Println(string(line))//{"cardTime":"2023-07-08 21:46:58","cardId":"tarot5.jpg"}
}
}
这下直接不敢确定原因了,就直接一个一个的读取文件,之后再去readline,代码如下:
func getLastOk() {
file, err := os.OpenFile("checkLog.txt", os.O_RDONLY, 0600)
if err != nil {
panic(err)
}
defer file.Close()
info, _ := file.Stat()
b := []byte{0}
if info.Size() > 0 {
index := int64(-1)
for {
index--
file.Seek(index, io.SeekEnd)
_, err := file.Read(b)
if b[0] == '\n' {
break
}
if err != nil {
if err == io.EOF {
break
}
fmt.Println(err)
}
}
rd := bufio.NewReader(file)
line, _, _ := rd.ReadLine()
fmt.Println(string(line))
}
}
这样也是可以正确读取文本的最后一行的,但是上面错误的原因还没完全搞清楚,而暂时用的这种方法是会频繁直接读取文件,理论上是不推荐的
更新
昨天翻到一篇讲述go的bufio包的文章,如下图,可能是bufio的缓存区非空,然后从最后一行开始往前移动seek指针不会读取到最后一行的\n,而读取的长度也比缓冲区小,导致缓冲区只更新了一部分也就读出来的长度的内容,readline又是读到\n,也就把残留的也一起读出来了。
打印一下最后的缓冲区内容:
func getLastERR() {
file, err := os.OpenFile("checkLog.txt", os.O_RDONLY, 0600)
if err != nil {
panic(err)
}
defer file.Close()
info, _ := file.Stat()
if info.Size() > 0 {
index := int64(-1)
r := bufio.NewReader(file)
for {
index--
file.Seek(index, io.SeekEnd)
readByte, err := r.ReadByte()
if readByte == '\n' {
//file.Seek(0, io.SeekEnd)
break
}
if err != nil {
if err == io.EOF {
break
}
fmt.Println(err)
}
}
bytes, _ := rd.Peek(1000)
fmt.Println(string(bytes))
line, _, _ := r.ReadLine()
fmt.Println(string(line))//{"cardTime":"2023-07-08 21:46:58","cardId":"tarot5.jpg"}
}
}
果然,第一行是残留了,并且会读取到 :"tarot3.jpg"}\n 的\n截止
{"cardTime":"2023-07-08 21:46:58","cardId":"tarot5.jpg"}:"tarot3.jpg"}
{"cardTime":"2023-07-08 21:46:58","cardId":"tarot5.jpg"}
但是为什么在构造bufio.reader的时候就不会残留还是不清楚,不知道是不是bug
r := bufio.NewReader(file)
bs,_:=r.Peek(4096)
fmt.Println(string(bs))//{"cardTime":"2023-07-08 21:46:51","cardId":"tarot3.jpg"}