Golangでcsvのパースをする時に、結構な頻度でハマること (reader.ReadAll()を使うと頻発する問題 strings.TrimSpaceを使うこと)

以下のGo言語プログラムで、"small2_bus_data.csv"が1行のみの
1, 93, 139.62957005198, 35.36604342344, 12:55:00
を使って、を読み込ませたのですが、その結果が
1 0 0 12:55:00 [{1 0001-01-01 00:00:00 +0000 UTC {0 0}}]
となってしまいます。

package main

import (
	"encoding/csv"
	"fmt"
	"os"
	"strconv"
	"time"
)

// 緯度経度の型定義
type LatLng struct {
	Lat, Lng float64
}

// 時間と緯度経度の情報を持つ構造体
type BusData struct {
	NodeID   int
	Time     time.Time
	Location LatLng
}

func main() {
	// CSVファイルを開く
	file, err := os.Open("small2_bus_data.csv")
	if err != nil {
		fmt.Println("Error:", err)
		return
	}
	defer file.Close()

	// CSVファイルの内容をパースする
	reader := csv.NewReader(file)
	records, err := reader.ReadAll()
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	// データを格納するためのスライス
	var busData []BusData

	// CSVの各行を処理する
	for _, record := range records {
		nodeID, _ := strconv.Atoi(record[0])
		lng, _ := strconv.ParseFloat(record[2], 64)
		lat, _ := strconv.ParseFloat(record[3], 64)
		timeStr := record[4]

		fmt.Println(nodeID, lng, lat, timeStr)

		// 時間のパース
		var parsedTime time.Time
		if timeStr != "" {
			parsedTime, _ = time.Parse("15:04:05", timeStr)
		}

		// データを構造体に格納
		data := BusData{
			NodeID: nodeID,
			Time:   parsedTime,
			Location: LatLng{
				Lat: lat,
				Lng: lng,
			},
		}
		busData = append(busData, data)
	}

	fmt.Println(busData)
}

で、かなり、すったもんだした結果、文字列に余分なスペースが含まれていたため であることが分かりました(このくらい自動で対処して欲しいが)。

strings.TrimSpace がキモだったようです。

修正後のプログラムは以下の通り。

package main

import (
	"encoding/csv"
	"fmt"
	"os"
	"strconv"
	"strings"
	"time"
)

// 緯度経度の型定義
type LatLng struct {
	Lat, Lng float64
}

// 時間と緯度経度の情報を持つ構造体
type BusData struct {
	NodeID   int
	Time     time.Time
	Location LatLng
}

func main() {
	// CSVファイルを開く
	file, err := os.Open("small2_bus_data.csv")
	if err != nil {
		fmt.Println("Error:", err)
		return
	}
	defer file.Close()

	// CSVファイルの内容をパースする
	reader := csv.NewReader(file)
	records, err := reader.ReadAll()
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	// データを格納するためのスライス
	var busData []BusData

	// CSVの各行を処理する
	for _, record := range records {
		nodeID, _ := strconv.Atoi(record[0])

		// スペースをトリムしてから実数に変換
		lng, err := strconv.ParseFloat(strings.TrimSpace(record[2]), 64)
		if err != nil {
			fmt.Println("Error parsing lng:", err)
			return
		}
		lat, err := strconv.ParseFloat(strings.TrimSpace(record[3]), 64)
		if err != nil {
			fmt.Println("Error parsing lat:", err)
			return
		}
		timeStr := strings.TrimSpace(record[4]) // スペースをトリム

		fmt.Println(nodeID, lng, lat, timeStr)

		// 時間のパース
		var parsedTime time.Time
		if timeStr != "" {
			parsedTime, err = time.Parse("15:04:05", timeStr)
			if err != nil {
				fmt.Println("Error parsing time:", err)
				return
			}
		}

		// データを構造体に格納
		data := BusData{
			NodeID: nodeID,
			Time:   parsedTime,
			Location: LatLng{
				Lat: lat,
				Lng: lng,
			},
		}
		busData = append(busData, data)
	}

	fmt.Println(busData)
}

出力結果は
>go run main28.go
1 139.62957005198 35.36604342344 12:55:00
[{1 0000-01-01 12:55:00 +0000 UTC {35.36604342344 139.62957005198}}]
となり、一安心です。

2024,江端さんの技術メモ

Posted by ebata