You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Golang中如何统计CSV文件每行的字节数?

如何准确统计CSV文件每行的原始字节数(包含逗号和换行符)

我有一个test.csv文件,需要逐行读取并统计每行的字节数。在Windows系统下,前两行带有\r\n换行符,文件总字节数应为37。使用csv.NewReader()统计时,发现该Reader仅统计了字段内容的字节数,未计入每行的逗号分隔符和换行符。目前想到的手动计算逗号数量再加换行符字节数的方法比较繁琐,想找更优方案。

现有代码如下:

package main

import (
    "encoding/csv"
    "fmt"
    "io"
    "log"
    "os"
)

func main() {
    file, err := os.Open("test.csv")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

    fileInfo, err := file.Stat()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("file total bytes is %d\n", fileInfo.Size())

    // init reader
    reader := csv.NewReader(file)

    // extract the header
    headers, err := reader.Read()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("headers are: %+v\n", headers)

    byteCounter := 0
    for {
        // if we reached end of file, stop
        if err == io.EOF {
            break
        }

        // read a record
        record, err := reader.Read()
        if err != nil {
            log.Fatal(err)
        }
        // loop through each record and count how many bytes
        for _, item := range record {
            byteCounter += len(item)
            fmt.Printf("record is %d bytes\n", len(item))
        }
        fmt.Println("total bytes so far is: ", byteCounter)

    }
}

问题分析

csv.NewReader的核心作用是解析CSV的字段内容,会自动过滤掉逗号分隔符、换行符等格式字符,所以用它统计的字节数必然缺失这些部分——这是设计特性而非问题。要统计每行的原始字节数,需要绕开CSV解析器的字段提取逻辑,或者结合文件偏移量来计算。


方案一:直接读取原始行字节(推荐,简单高效)

使用bufio.Reader的ReadBytes('\n')方法直接读取每行的原始字节流,该方法会返回包含换行符(Windows下为\r\n)的整行数据,直接统计返回字节切片的长度即可得到该行的实际字节数,包括逗号和换行符。

代码示例:

package main

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

func main() {
    file, err := os.Open("test.csv")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

    fileInfo, err := file.Stat()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("file total bytes is %d\n", fileInfo.Size())

    reader := bufio.NewReader(file)
    totalBytes := 0
    lineNum := 0

    for {
        // 读取包含换行符的整行字节
        line, err := reader.ReadBytes('\n')
        if err != nil {
            if err.Error() != "EOF" {
                log.Fatal(err)
            }
            // 处理最后一行无换行符的情况
            if len(line) > 0 {
                lineNum++
                lineBytes := len(line)
                totalBytes += lineBytes
                fmt.Printf("第%d行:%d字节\n", lineNum, lineBytes)
            }
            break
        }
        lineNum++
        lineBytes := len(line)
        totalBytes += lineBytes
        fmt.Printf("第%d行:%d字节\n", lineNum, lineBytes)
    }

    fmt.Printf("统计总字节数:%d\n", totalBytes)
}

方案二:解析CSV字段同时统计原始行字节

如果需要同时解析CSV字段内容,又要统计每行的原始字节数,可以通过记录文件的读写偏移量来计算。每次调用csv.Read()前后获取文件当前位置,两者的差值就是该行原始数据的字节数。

代码示例:

package main

import (
    "encoding/csv"
    "fmt"
    "io"
    "log"
    "os"
)

func main() {
    file, err := os.Open("test.csv")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

    fileInfo, err := file.Stat()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("file total bytes is %d\n", fileInfo.Size())

    reader := csv.NewReader(file)
    // 禁用注释解析,避免误判行内容
    reader.Comment = 0

    totalBytes := 0
    lineNum := 0

    for {
        // 记录读取前的文件位置
        posBefore, err := file.Seek(0, io.SeekCurrent)
        if err != nil {
            log.Fatal(err)
        }

        record, err := reader.Read()
        if err != nil {
            if err == io.EOF {
                // 处理最后一行的剩余字节
                posEnd, _ := file.Seek(0, io.SeekCurrent)
                if posEnd > posBefore {
                    lineNum++
                    lineBytes := posEnd - posBefore
                    totalBytes += int(lineBytes)
                    fmt.Printf("第%d行:%d字节,字段:%+v\n", lineNum, lineBytes, record)
                }
                break
            }
            log.Fatal(err)
        }

        // 记录读取后的文件位置
        posAfter, err := file.Seek(0, io.SeekCurrent)
        if err != nil {
            log.Fatal(err)
        }

        lineNum++
        lineBytes := posAfter - posBefore
        totalBytes += int(lineBytes)
        fmt.Printf("第%d行:%d字节,字段:%+v\n", lineNum, lineBytes, record)
    }

    fmt.Printf("统计总字节数:%d\n", totalBytes)
}

总结

  • 若仅需统计每行字节数,方案一最直接高效,无需处理CSV解析逻辑;
  • 若需同时解析CSV字段和统计字节,方案二通过文件偏移量计算,能准确获取每行原始字节数,且保留CSV解析能力。

内容的提问来源于stack exchange,提问作者Coldchain9

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 01:15:23