Sibainu Relax Room

柴犬と過ごす

CSV を GO でクィックソート

柴犬の友達を見つめ早くこちらに来いかなあと思っているのでした。

今回の概要

10月19日投稿の流れで、今回同じレコード数のCSVファイル「 input.csv 」を読み込み、昇順でソートし、「 output.csv 」で書き出すということをしてみました。

ソート処理にかかる時間は同じとし、ファイルの読み込みと書き出しがあるので、前回より遅いと予想していました。

しかし、読み込みから書き出しまでかかった時間はわずか3秒という予想外のものでした。

読み込みファイル

ファイルは、前回と同じ104万件のデータをCSV形式にしたものを使いました。

左が元データのCSVファイルをEXCELで読み込んだ画像です。

右がソートした結果のファイルを同じくEXCELで読み込んだものです。

input.csv は12メガありかなりおおきなファイルとなりました。

main.go のソースコード

「 input.csv 」の文字コードは今風の UTF-8 ではなくANSIで作ったものにしました。

したがって、「デコード」ANSI→UTF-8、「エンコード」UTF-8→ANSIと一手間かかるものとなりました。

copy

package main

import (
    "encoding/csv"
    "log"
    "os"
    "strings"

    "golang.org/x/text/encoding/japanese"
    "golang.org/x/text/transform"
)

func QuickSort(elements []string, left int, right int) {
    if left < right {
        posi := getposi(elements, left, right)
        QuickSort(elements, left, posi-1)
        QuickSort(elements, posi+1, right)
    }
}

func getposi(elements []string, left int, right int) int {
    //軸を右端
    pivot := elements[right]
    //開始位置
    i :=  left
    //昇順にするため左に小さいもののエリア
    for j := left; j < right; j++ {
        //pivotより小さいものが見つかった
        if strings.Compare(pivot, elements[j]) == 1 {
            swap(&elements[i], &elements[j])
            //次の交換位置
            i += 1
        }
    }
    //pivotの位置iが定まったので交換
    swap(&elements[i], &elements[right])
    //位置を返す
    return i
}

func swap(element1 *string, element2 *string) {
    val := *element1
    *element1 = *element2
    *element2 = val
}

func main() {
    in, err := os.Open("./input.csv")

    if err != nil {
        log.Fatal(err)
    }
    defer in.Close()

    r := csv.NewReader(transform.NewReader(in, japanese.ShiftJIS.NewDecoder()))
    // records => [][]string
    records, err := r.ReadAll()
    if err != nil {
        log.Fatal(err)
    }

    //[][0]string => []string
    p := make([]string, len(records))
    for i := 0; i < len(records); i++ {
        p[i] = records[i][0]
    }

     QuickSort(p, 0, len(p)-1)

     //[]string => [][0]string
     q := make([][]string, len(records))
     for i := 0; i < len(records); i++ {
          q[i] = make([]string, 1)
          q[i][0] = p[i]
     }

     // 書き込み先ファイル
     out, err := os.Create("./output.csv")
     if err != nil {
          log.Fatal(err)
     }
     defer out.Close()

     // 書き込み
     w := csv.NewWriter(transform.NewWriter(out, japanese.ShiftJIS.NewEncoder()))
     w.WriteAll(q)
}

実行

コマンド プロンプトから「 main.go 」のあるディレクトリに移動します。

C:\Users\sora>cd C:\Users\ユーザー名\learning\go\test

ビルド(実行ファイル test.exe が作成されます。)します。

C:\Users\ユーザー名\learning\go\test>go build

ビルドして作成した実行ファイル(この場合 test.exe )を実行します。

C:\Users\ユーザー名\learning\go\test>test.exe

なぜか「 output.csv 」が小さくなった。