Golang读取文件时如何跳过最后一行

Golang读取文件时如何跳过最后一行 我有一个包含6000多行的大文件。最后一行是时间戳,当解析到这一行时会出现致命错误。如何在遇到这个时间戳时停止数据解析,或者直接跳过文件的最后一行?

8 回复

我通过 scanner.Scan() 跳过了文档的第一行,因为第一行没有有用数据。

我使用的是 bufio.Scanner。我的代码如下:

scanner := bufio.NewScanner(GPSData)
scanner.Scan()
for scanner.Scan() {
   if reisaiDataRow, err = rp.allocateFile1Neo(scanner.Text()); err != nil {
      return nil, fmt.Errorf("failed allocate: %s", err.Error())
   }
   if reisaiDataRow == nil {
      continue
   }
   reisaiMap[reisaiDataRow.BusNumber] = reisaiDataRow
}

更多关于Golang读取文件时如何跳过最后一行的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我看到这个问题的两种解决方案,第一种是通过错误处理,正如 @Christophe_Meessen 回答的那样,但如果你在文件中间遇到这个错误,就无法解决。第二种是每次迭代读取两行,如果第二行是EOF就跳过它。

我按照你的建议有了这个想法

只有当数组长度为11时我才解析数据,这意味着当遇到时间戳时数组长度为1,这时就会跳过它

// 代码示例
func main() {
    // 这里放置Go代码
}

image

当我应用您的建议时出现这个错误。它仍然读取时间戳吗?

var ErrNotEnoughFields = errors.New("not enough fields")

我们需要更多信息来帮助你。你遇到的错误是什么?rp.allocateFile1Neo 是做什么的?

与其直接将 scanner.Text() 传递给 rp.allocateFile1Neo,你可以将其赋值给一个变量,并在将值传递给 rp.allocateFile1Neo 之前检查变量内容是否有效。

scanner := bufio.NewScanner(GPSData)
scanner.Scan()
for scanner.Scan() {
    line := scanner.Text()
    if !isValid(line) {
        continue
    } 
    if reisaiDataRow, err = rp.allocateFile1Neo(line); err != nil {
        return nil, fmt.Errorf(“failed allocate: %s”, err.Error())
    }
    if reisaiDataRow == nil {
        continue
    }
    reisaiMap[reisaiDataRow.BusNumber] = reisaiDataRow
}

你说得对。问题在于使用分割或逗号时,由于最后一行没有逗号,你无法获得包含多个元素的切片。这样指令 r.Route = arr[1] 就会因为索引越界而失败。

一个简单的解决方案是测试 arr 切片的长度。如果长度不符合预期,说明字段缺失或者你正处于仅包含日期的文件最后一行。因此你可以改用以下代码:

arr := strings.Split(data, “,”)
if len(arr) != 3 {
    return nil, ErrNotEnoughFields
}
r = new(ReisaiDataRow)
r.VehicleType = arr[0]
r.Route = arr[1]
if r.Schedule, err = strconv.Atoi(arr[2]); err != nil {
    return nil, err
}

作为全局变量,你可以定义:

var ErrNotEnoughFields = errors.New("not enough fields")

然后你可以将 allocateFile1Neo 返回的错误与此错误值进行比较,以判断是否应该跳过该行,或者是否存在需要以不同方式处理的其他类型错误。

我将重新表述我的问题。

我有一个超过6000行的文件,第一行是说明性文字 示例: 车牌号,目的地 等

后续行是数据 示例: 46779978,考纳斯 等

总共有11个数据字段,全部用逗号分隔。我使用以下代码从文件中读取这些数据:

scanner := bufio.NewScanner(GPSData)
scanner.Scan()
for scanner.Scan() {
   if reisaiDataRow, err = rp.allocateFile1Neo(scanner.Text()); err != nil {
      return nil, fmt.Errorf("failed allocate: %s", err.Error())
   }
   if reisaiDataRow == nil {
      continue
   }
   reisaiMap[reisaiDataRow.BusNumber] = reisaiDataRow
}

allocateFile1Neo 是数据解析函数,如下所示:

arr := strings.Split(data, ",")
r = new(ReisaiDataRow)

r.VehicleType = arr[0]
r.Route = arr[1]
if r.Schedule, err = strconv.Atoi(arr[2]); err != nil {
   return nil, err
}

数据的最后一行是时间戳,格式如下:2019-10-15 18:42:00

我遇到了以下错误:

image

我认为问题在于这一行完全没有逗号,但程序仍然尝试解析它?如何让我的程序识别出这是时间戳,或者直接跳过文件的最后一行?

在Go语言中,可以通过多种方式跳过文件的最后一行。以下是一个高效的方法,使用bufio.Scanner逐行读取文件,并在遇到最后一行(时间戳)时停止处理。这种方法适用于大文件,因为它不会将整个文件加载到内存中。

示例代码:

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    file, err := os.Open("yourfile.txt") // 替换为你的文件路径
    if err != nil {
        fmt.Printf("打开文件时出错: %v\n", err)
        return
    }
    defer file.Close()

    scanner := bufio.NewScanner(file)
    var lines []string

    // 逐行读取文件
    for scanner.Scan() {
        lines = append(lines, scanner.Text())
    }

    if err := scanner.Err(); err != nil {
        fmt.Printf("读取文件时出错: %v\n", err)
        return
    }

    // 检查是否有行,然后跳过最后一行
    if len(lines) > 0 {
        lines = lines[:len(lines)-1] // 移除最后一行
    }

    // 处理剩余的行(例如,解析数据)
    for _, line := range lines {
        fmt.Println(line) // 这里替换为你的解析逻辑
    }
}

如果文件非常大(例如6000多行),并且你不想在内存中存储所有行,可以逐行处理并在倒数第二行停止。但这种方法需要知道总行数或检测最后一行。以下是使用计数器的方法,假设你知道总行数(例如6000行):

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    file, err := os.Open("yourfile.txt") // 替换为你的文件路径
    if err != nil {
        fmt.Printf("打开文件时出错: %v\n", err)
        return
    }
    defer file.Close()

    scanner := bufio.NewScanner(file)
    lineCount := 0
    totalLines := 6000 // 假设总行数为6000,跳过最后一行

    for scanner.Scan() {
        lineCount++
        if lineCount >= totalLines {
            break // 在最后一行之前停止
        }
        line := scanner.Text()
        // 处理行数据(例如,解析)
        fmt.Println(line) // 这里替换为你的解析逻辑
    }

    if err := scanner.Err(); err != nil {
        fmt.Printf("读取文件时出错: %v\n", err)
        return
    }
}

如果不知道总行数,但最后一行是时间戳(例如,可以通过正则表达式识别),可以在读取时检查每行内容,并在遇到时间戳时停止:

package main

import (
    "bufio"
    "fmt"
    "os"
    "regexp"
)

func main() {
    file, err := os.Open("yourfile.txt") // 替换为你的文件路径
    if err != nil {
        fmt.Printf("打开文件时出错: %v\n", err)
        return
    }
    defer file.Close()

    scanner := bufio.NewScanner(file)
    timestampRegex := regexp.MustCompile(`^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$`) // 示例时间戳格式,根据你的文件调整

    for scanner.Scan() {
        line := scanner.Text()
        if timestampRegex.MatchString(line) {
            break // 遇到时间戳时停止解析
        }
        // 处理行数据
        fmt.Println(line) // 这里替换为你的解析逻辑
    }

    if err := scanner.Err(); err != nil {
        fmt.Printf("读取文件时出错: %v\n", err)
        return
    }
}

这些方法可以根据你的具体需求调整。如果文件结构固定,第一种方法(移除最后一行)最简单;如果最后一行可识别,使用正则表达式更灵活。

回到顶部