在 Golang 中统计文件行数

在 Golang 中统计文件行数可以通过多种方式实现,以下是几种常见方法及适用场景分析:

一、基础实现方案

方案1:逐行读取(适合小文件)

func CountLines(filePath string) (int, error) {
    file, err := os.Open(filePath)
    if err != nil {
        return 0, err
    }
    defer file.Close()

    scanner := bufio.NewScanner(file)
    count := 0
    for scanner.Scan() {
        count++
    }

    if err := scanner.Err(); err != nil {
        return 0, err
    }
    return count, nil
}

特点

  • 内存友好(逐行处理)
  • 自动处理各种换行符(\n/\r\n)
  • 适合行数较少(<10万行)的场景

方案2:批量读取(适合大文件)

func CountLinesBulk(filePath string) (int, error) {
    data, err := os.ReadFile(filePath)
    if err != nil {
        return 0, err
    }

    lines := strings.Split(string(data), "\n")
    // 处理末尾可能的空行
    if len(lines) > 0 && strings.TrimSpace(lines[len(lines)-1]) == "" {
        lines = lines[:len(lines)-1]
    }
    return len(lines), nil
}

特点: 一次性读取文件,I/O次数少 内存消耗较高(需加载整个文件) 适合大文件(>100MB)场景

二、进阶优化方案

方案3:带缓冲的流式处理

func CountLinesBuffered(filePath string) (int, error) {
    file, err := os.Open(filePath)
    if err != nil {
        return 0, err
    }
    defer file.Close()

    buf := make([]byte, 4096)
    count := 0
    for {
        n, err := file.Read(buf)
        if err != nil && err != io.EOF {
            return 0, err
        }

        for i := 0; i < n; {
            if buf[i] == '\n' || buf[i] == '\r' {
                count++
                // 跳过可能的回车+换行组合
                if i+1 < n && buf[i] == '\r' && buf[i+1] == '\n' {
                    i += 2
                } else {
                    i += 1
                }
            } else {
                i++
            }
        }
    }
}

特点: 自定义缓冲区大小(默认4KB) 处理所有换行符变体(\n/\r/\r\n) 内存消耗可控,适合超大文件

四、特殊场景处理

1.超时控制(结合Context):

func CountLinesWithTimeout(filePath string, timeout time.Duration) (int, error) {
    ctx, cancel := context.WithTimeout(context.Background(), timeout)
    defer cancel()

    file, err := os.Open(filePath)
    if err != nil {
        return 0, err
    }
    defer file.Close()

    scanner := bufio.NewScanner(file)
    count := 0
    for {
        select {
        case <-ctx.Done():
            return 0, ctx.Err()
        default:
            if scanner.Scan() {
                count++
            } else {
                break
            }
        }
    }
    return count, scanner.Err()
}

2.大文件分片处理(适合超过内存限制的场景):

func CountLinesInChunks(filePath string, chunkSize int) (int, error) {
    file, err := os.Open(filePath)
    if err != nil {
        return 0, err
    }
    defer file.Close()

    count := 0
    offset := 0
    for {
        buf := make([]byte, chunkSize)
        n, err := file.ReadAt(buf, offset)
        if err == io.EOF {
            break
        }
        if err != nil {
            return 0, err
        }

        count += countNewlines(buf)
        offset += n
    }
    return count, nil
}

func countNewlines(buf []byte) int {
    count := 0
    for i := 0; i < len(buf); i++ {
        if buf[i] == '\n' || buf[i] == '\r' {
            count++
            // 跳过回车+换行组合
            if i+1 < len(buf) && buf[i] == '\r' && buf[i+1] == '\n' {
                i++
            }
        }
    }
    return count
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值