LoginSignup
1
0

More than 5 years have passed since last update.

スターティングGo言語7章のメモ - os/time

Last updated at Posted at 2019-02-03

Goの標準パッケージを入門レベルで学ぼうと思って書き残したメモです。
スターティングGo言語の7章の以下のパッケージについて記載しています。
単なる写経に近いクソ記事ですw。

  • osパッケージ
  • timeパッケージ

osパッケージ

import "os"

ホスト名の取得

func main() {
    host, _ := os.Hostname()
    fmt.Println(host)
}

環境変数

func main() {
    // 環境変数一覧を表示
    for _, env := range os.Environ(){
        fmt.Println(env)
    }

    // 環境変数の名前を指定して取得
    fmt.Println(os.Getenv("HOME"))
}

プログラムの終了

func main() {
    // 終了コード1で終了する
    os.Exit(1)
}

プロセス情報

func main() {
    fmt.Printf("プロセスID: %v\n", os.Getpid())
    fmt.Printf("親プロセスID: %v\n",os.Getppid())
    fmt.Printf("ユーザID: %v\n", os.Getuid())
    fmt.Printf("グループID: %v\n", os.Getgid())
}

log.Fatal

func main()  {
    _, err := os.Open("README.md")
    if err != nil {
        log.Fatal(err)
    }
}

コマンドライン引数

func main()  {
    for _, v := range os.Args {
        fmt.Println(v)
    }
}

ファイル操作

読み込み専用ファイルのオープン

func main()  {
    f, err := os.Open("README.md")
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
}

os.File

func main()  {
    f, err := os.Open("README.md")
    if err != nil {
        log.Fatal(err)
    }

    /* []byte型のスライスにファイルの内容を読み込む */
    bs1 := make([]byte,128)
    n ,err := f.Read(bs1) // nは実際に読み込んだバイト数
    if err != nil {
        log.Fatalln(err)
    }
    fmt.Println(n)

    /* ファイルのオフセットを指定して読み込む */
    bs2 := make([]byte, 128)
    n2, err := f.ReadAt(bs2, 10) // 10バイト目から読み込む
    if err != nil {
        log.Println(err)
    }
    fmt.Println(n2)

    /* ファイルのステータス取得 */
    fi, err := f.Stat()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(fi.Name()) // ファイル名
    fmt.Println(fi.Size()) // ファイルサイズ
    fmt.Println(fi.Mode()) // ファイルのモード
    fmt.Println(fi.ModTime()) // ファイルの最終更新時間
    fmt.Println(fi.IsDir()) // ディレクトリかどうか

}

実際にファイルを読み込む処理を書くと、こんなかんじでしょうか。

func readFile(filepath string) (lines []string) {
    f, err := os.Open(filepath)

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

    buf := make([]byte, 1024)

    for {
        n, err := f.Read(buf)
        if err != nil {
            log.Fatal(err)
        }
        if err == io.EOF { // EOFの場合抜ける
            break
        }
        str := string(buf[:n])
        fmt.Println(str)
        lines = append(lines, str)
    }
    return
}

func main() {
    filepath := "README.md"

    for _, line := range readFile(filepath) {
        fmt.Println(line)
    }
}

新規ファイルの作成

func main() {
    f, err := os.Create("NewFile.txt") // ファイル名を指定して新規作成
    if err != nil {
        log.Fatal(err)
    }

    f.Write([]byte("Hello, World\n")) // ファイルに[]byte型の内容を書き込み
    f.WriteAt([]byte("Golang"), 7)    // オフセットを指定して書き込み
    f.Seek(0, io.SeekEnd)             // ファイルの末尾にオフセットを移動
    f.WriteString("Year")             // 文字列をファイルに書き込み
}

ファイルオープン詳細

os.OpenFileを使う。

func main() {
    f, err := os.OpenFile("README.md", os.O_RDONLY, 0666)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(f.Name())
    defer f.Close()
}
フラグ 意味
O_RDONLY 読み込み専用
O_WRONLY 書き込み専用
O_RDWR 読み書き可能
O_APPEND ファイルの末尾に追記
O_CREATE ファイルが存在しなければ新規作成
O_TRUNC 可能であればファイルの内容をオープン時に空にする

ファイルの削除

func main() {
    if err := os.Remove("NewFile.txt"); err != nil {
        log.Fatal(err)
    }
}

ファイル名の変更と移動

func main() {
    if err := os.Rename("NewFile.txt", "BackupFile.txt"); err != nil {
        log.Fatal(err)
    }
}

ディレクトリ操作

カレントディレクトリ

func main() {
    dir, err := os.Getwd()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(dir)
}

ディレクトリの読み込み

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

    // カレントディレクトリ下のディレクトリ名を列挙
    fis, err := f.Readdir(0)
    for _, fi := range fis {
        if fi.IsDir() {
            fmt.Println(fi.Name())
        }
    }
}

ディレクトリの作成

func main() {
    // カレントディレクトリ配下にディレクトリを作成
    if err := os.Mkdir("foo", 0775); err != nil {
        log.Fatal(err)
    }

    // カレントディレクトリ配下にディレクトリを一括作成
    // mkdir -p に相当
    if err := os.MkdirAll("foo/bar/baz", 0755); err != nil {
        log.Fatal(err)
    }
}

ディレクトリの削除

func main() {
    if err := os.Remove("NewFile.txt"); err != nil {
        log.Fatal(err)
    }

    if err := os.RemoveAll("foo") err != nil {
        log.Fatal(err)
    }
}

その他のファイル操作

シンボリックリンクの操作

func main() {
    // シンボリックリンクbar.txtを作成
    if err := os.Symlink("foo.txt", "bar.txt"); err != nil {
        fmt.Println(err)
    }

    // シンボリックリンクのリンク先を読み込む
    path, err := os.Readlink("bar.txt")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(path)
}

timeパッケージ

import "time"

現在の時刻取得

func main() {
    t := time.Now()
    fmt.Println(t)
}

指定した時刻の生成

func main() {
    t := time.Date(2019, 2, 3, 11, 50, 0, 0, time.Local)
    fmt.Println(t)
    fmt.Println(t.Year())
    fmt.Println(t.Month())
    fmt.Println(t.Day())
    fmt.Println(t.Weekday())
    fmt.Println(t.Hour())
    fmt.Println(t.Minute())
    fmt.Println(t.Second())
    fmt.Println(t.Nanosecond())
    fmt.Println(t.Zone())
}
メソッド 戻り値の型 意味
Year int
YearDay int 1~366
Month time.Month
Weekday time.Weekday 曜日
Day int 1~31
Hour int 0~23
Minute int 0~59
Second int 0~59
NanoSecond int ナノ秒
Zone string, int タイムゾーンとオフセット秒

時刻間隔の表現

func main() {
    fmt.Println(time.Hour)        // 1h0m0s
    fmt.Println(time.Minute)      // 1m0s
    fmt.Println(time.Second)      // 1s
    fmt.Println(time.Millisecond) // 1ms
    fmt.Println(time.Microsecond) // 1µs
    fmt.Println(time.Nanosecond)  // 1ns
}

文字列からtime.Durationを生成

func main() {
    // 文字列からtime.Durationを生成
    duration, err := time.ParseDuration("2h30m")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%v : %T", duration, duration) // 2h30m0s : time.Duration
}

time.Durationの計算

func main() {
    t := time.Now()
    fmt.Println(t) // 2019-02-03 12:01:38.307595 +0900 JST m=+0.000568779
    t = t.Add(2*time.Minute + 15*time.Microsecond)
    fmt.Println(t) // 2019-02-03 12:03:38.30761 +0900 JST m=+120.000583779
}

時刻の差分を取得

func main() {
    t0 := time.Date(2020, 2,1,0,0,0,0,time.Local)
    t1 := time.Now()

    d := t0.Sub(t1)
    fmt.Println(d) // 8699h55m41.548192s
}

時刻の比較

func main() {
    t0 := time.Now()
    t1 := t0.Add(24 * time.Hour)
    fmt.Println(t0) // 2019-02-03 12:09:01.320207 +0900 JST m=+0.000428654
    fmt.Println(t1) // 2019-02-04 12:09:01.320207 +0900 JST m=+86400.000428654

    // 時刻の比較
    fmt.Println(t1.Before(t0)) // false
    fmt.Println(t1.After(t0))  // true
}

年月日の増減

func main() {
    t0 := time.Date(2019,2,1,0,0,0,0,time.Local)
    fmt.Println(t0) // 2019-02-01 00:00:00 +0900 JST

    // 1年増やす
    t1 := t0.AddDate(1, 0,0)
    fmt.Println(t1) // 2020-02-01 00:00:00 +0900 JST

    // 1ヶ月減らす
    t2 := t0.AddDate(0,-1,0)
    fmt.Println(t2) // 2019-01-01 00:00:00 +0900 JST
}

文字列からの時刻生成

func main() {
    // 第一引数にフォーマットを指定
    // 第二引数がパース対象の文字列
    t, err := time.Parse(time.RFC822, "02 Jan 06 15:04 MST")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(t) // 2006-01-02 15:04:00 +0000 MST
}

第一引数のフォーマットは以下の通り定義されている。

const (
    ANSIC       = "Mon Jan _2 15:04:05 2006"
    UnixDate    = "Mon Jan _2 15:04:05 MST 2006"
    RubyDate    = "Mon Jan 02 15:04:05 -0700 2006"
    RFC822      = "02 Jan 06 15:04 MST"
    RFC822Z     = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone
    RFC850      = "Monday, 02-Jan-06 15:04:05 MST"
    RFC1123     = "Mon, 02 Jan 2006 15:04:05 MST"
    RFC1123Z    = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone
    RFC3339     = "2006-01-02T15:04:05Z07:00"
    RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
    Kitchen     = "3:04PM"
    // Handy time stamps.
    Stamp      = "Jan _2 15:04:05"
    StampMilli = "Jan _2 15:04:05.000"
    StampMicro = "Jan _2 15:04:05.000000"
    StampNano  = "Jan _2 15:04:05.000000000"
)

自分でフォーマットを定義する場合は、例えば以下のようにする。
注意することとして、フォーマット指定のタイムスタンプは2006年1月2日 15:04:05で統一されていないといけない。

このフォーマット指定に疑問を抱いたので調べたところ、アメリカの時刻の順番らしい。”1月2日午後3時4分5秒2006年”でとなっていて、1,2,3,4,5,6と並んでいる。

func main() {
    t, err := time.Parse("2006年1月2日 15時04分05秒", "2019年2月3日 21時15分00秒")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(t) // 2019-02-03 21:15:00 +0000 UTC
}

時刻からの文字列生成

func main() {
    t := time.Now()
    f1 := t.Format(time.RFC822)
    f2 := t.Format(time.RFC3339)
    f3 := t.Format("2006年1月2日 15時43分28秒")
    f4 := t.Format("2006/01/02")

    fmt.Printf("f1: %v (%T)\n", f1, f1) // f1: 03 Feb 19 14:05 JST (string)
    fmt.Printf("f2: %v (%T)\n", f2, f2) // f2: 2019-02-03T14:05:05+09:00 (string)
    fmt.Printf("f3: %v (%T)\n", f3, f3) // f3: 2019年2月3日 14時52分38秒 (string)
    fmt.Printf("f4: %v (%T)\n", f4, f4) // f4: 2019/02/03 (string)
}

時刻のUTC変換

func main() {
    t := time.Now()
    utc := t.UTC() // UTCに変換
    fmt.Printf("utc : %v (%T)\n", utc, utc) // utc : 2019-02-03 05:07:07.324682 +0000 UTC (time.Time)
}

時刻のローカルタイム変換

func main() {
    t := time.Now()
    jst := t.Local() // ローカルタイムに変換
    fmt.Printf("jst : %v (%T)\n", jst, jst) // jst : 2019-02-03 14:08:24.109525 +0900 JST (time.Time)
}

UNIX時間との相互変換

func main() {
    t := time.Now()
    unix := t.Unix()
    fmt.Printf("unix : %v (%T)\n", unix, unix) // unix : 1549170553 (int64)
}

指定時間のスリープ

func main() {
    for i := 0; i < 10; i ++ {
        fmt.Println(i)
        time.Sleep(100 * time.Millisecond) // 100msecスリープ
    }
}

time.Tick

time.Tickは、指定した時間間隔ごとに現在時刻を表すtime.Time型の値が送信されるチャネルを生成する。

func main() {
    // 3秒間隔で現在の時刻を送信するチャネルを定義
    ch := time.Tick(3 * time.Second)

    // 無限ループ
    for {
        t := <-ch
        fmt.Println(t) // 3秒間隔で表示される
    }
}

time.After

time.Afterは、チャネルに対して指定した時間間隔後に一度だけ現在時刻を表すtime.Timeを送信する。

func main() {
    // 5秒後に時刻を送信するチャネル
    ch := time.After(5 * time.Second)

    // 5秒後に表示される
    v := <-ch
    fmt.Println(v)
}

A Tour of Goのサンプルではtime.Ticktime.Afterを組み合わせてselect-for文で以下のような使い方をしていた。

func main() {
    tick := time.Tick(100 * time.Millisecond)
    boom := time.After(500 * time.Millisecond)
    for {
        select {
        case <-tick: // tickチャネルから受信したの場合
            fmt.Println("tick.")
        case <-boom: // boomチャネルから受信した場合はreturnでfor文から抜ける。
            fmt.Println("BOOM!")
            return
        default: // デフォルトの場合は50msecスリープ
            fmt.Println("    .")
            time.Sleep(50 * time.Millisecond)
        }
    }
}

今週は週3回のクソ記事投稿目標をなんとか達成。。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0