构建与测试colStats工具:实现CSV数据列统计功能
立即解锁
发布时间: 2025-09-14 00:07:59 阅读量: 3 订阅数: 38 AIGC 

### 构建与测试 colStats 工具:实现 CSV 数据列统计功能
在数据处理中,我们常常需要对 CSV 文件中的特定列进行统计计算,如求和或求平均值。为了实现这一功能,我们可以构建一个名为 colStats 的工具。下面将详细介绍该工具的开发与测试过程。
#### 1. 开发 colStats 工具
colStats 工具接收两个可选输入参数,每个参数都有默认值:
- `-col`:要执行操作的列,默认为第 1 列。
- `-op`:要在所选列上执行的操作,初始支持 `sum`(求和)和 `avg`(求平均值)两种操作,后续可按需添加更多操作。
此外,该工具还接受任意数量的文件名作为输入。如果用户提供多个文件名,工具会合并所有文件中同一列的结果。
以下是具体的操作步骤:
1. **创建项目目录并初始化 Go 模块**:
```bash
$ mkdir -p $HOME/pragprog.com/rggo/performance/colStats
$ cd $HOME/pragprog.com/rggo/performance/colStats
$ go mod init pragprog.com/rggo/performance/colStats
```
2. **创建错误定义文件 `errors.go`**:
```go
package main
import "errors"
var (
ErrNotNumber = errors.New("Data is not numeric")
ErrInvalidColumn = errors.New("Invalid column number")
ErrNoFiles = errors.New("No input files")
ErrInvalidOperation = errors.New("Invalid operation")
)
```
3. **创建 CSV 数据处理文件 `csv.go`**:
```go
package main
import (
"encoding/csv"
"fmt"
"io"
"strconv"
)
func sum(data []float64) float64 {
sum := 0.0
for _, v := range data {
sum += v
}
return sum
}
func avg(data []float64) float64 {
return sum(data) / float64(len(data))
}
// statsFunc defines a generic statistical function
type statsFunc func(data []float64) float64
func csv2float(r io.Reader, column int) ([]float64, error) {
cr := csv.NewReader(r)
column--
allData, err := cr.ReadAll()
if err != nil {
return nil, fmt.Errorf("Cannot read data from file: %w", err)
}
var data []float64
for i, row := range allData {
if i == 0 {
continue
}
if len(row) <= column {
return nil, fmt.Errorf("%w: File has only %d columns", ErrInvalidColumn, len(row))
}
v, err := strconv.ParseFloat(row[column], 64)
if err != nil {
return nil, fmt.Errorf("%w: %s", ErrNotNumber, err)
}
data = append(data, v)
}
return data, nil
}
```
4. **创建主函数文件 `main.go`**:
```go
package main
import (
"flag"
"fmt"
"io"
"os"
)
func main() {
op := flag.String("op", "sum", "Operation to be executed")
column := flag.Int("col", 1, "CSV column on which to execute operation")
flag.Parse()
if err := run(flag.Args(), *op, *column, os.Stdout); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func run(filenames []string, op string, column int, out io.Writer) error {
var opFunc statsFunc
if len(filenames) == 0 {
return ErrNoFiles
}
if column < 1 {
return fmt.Errorf("%w: %d", ErrInvalidColumn, column)
}
switch op {
case "sum":
opFunc = sum
case "avg":
opFunc = avg
default:
return fmt.Errorf("%w: %s", ErrInvalidOperation, op)
}
consolidate := make([]float64, 0)
for _, fname := range filenames {
f, err := os.Open(fname)
if err != nil {
return fmt.Errorf("Cannot open file: %w", err)
}
data, err := csv2float(f, column)
if err != nil {
return err
}
if err := f.Close(); err != nil {
return err
}
consolidate = append(consolidate, data...)
}
_, err := fmt.Fprintln(out, opFunc(consolidate))
return err
}
```
#### 2. 编写测试用例
为了确保 colStats 工具的正确性,我们需要编写一系列测试用例。以下是具体的测试步骤:
1. **创建单元测试文件 `csv_test.go`**:
```go
package main
import (
"bytes"
"errors"
"fmt"
"io"
"testing"
"testing/iotest"
)
func TestOperations(t *testing.T) {
data := [][]float64{
{10, 20, 15, 30, 45, 50, 100, 30},
{5.5, 8, 2.2, 9.75, 8.45, 3, 2.5, 10.25, 4.75, 6.1, 7.67, 12.287, 5.47},
{-10, -20},
{102, 37, 44, 57, 67, 129},
}
testCases := []struct {
name string
op statsFunc
exp []float64
}{
{"Sum", sum, []float64{300, 85.927, -30, 436}},
{"Avg", avg, []float64{37.5, 6.609769230769231, -15, 72.666666666666666}},
}
for _, tc := range testCases {
for k, exp := range tc.exp {
name := fmt.Sprintf("%sData%d", tc.name, k)
t.Run(name, func(t *testing.T) {
res := tc.op(data[k])
if res != exp {
t.Errorf("Expected %g, got %g instead", exp, res)
}
})
}
}
}
func TestCSV2Float(t *testing.T) {
csvData := `IP Address,Requests,Response Time
192.168.0.199,2056,236
192.168.0.88,899,220
192.168.0.199,3054,226
192.168.0.100,4133,218
192.168.0.199,950,238
`
testCases := []struct {
name string
col int
exp []float64
expErr error
r io.Reader
}{
{name: "Column2", col: 2,
exp: []float64{2056, 899, 3054, 4133, 950},
expErr: nil,
r: bytes.NewBufferString(csvData),
},
{name: "Column3", col: 3,
exp: []float64{236, 220, 226, 218, 238},
expErr: nil,
r: bytes.NewBufferString(csvData),
},
{name: "FailRead", col: 1,
exp: nil,
expErr: iotest.ErrTimeout,
r: iotest.TimeoutReader(bytes.NewReader([]byte{0})),
},
{name: "FailedNotNumber", col: 1,
exp: nil,
expErr: ErrNotNumber,
r: bytes.NewBufferString(csvData),
},
{name: "FailedInvalidColumn", col: 4,
exp: nil,
expErr: ErrInvalidCo
```
0
0
复制全文
相关推荐









