kurumi-bioの雑記帳

プログラミング、パソコン、ペット、 犬、お出かけ

初心者のGo言語 その10 <fmt.ScanとScanf、Scanln>

こんにちは、kurumi-bioです。13回目のブログです。
Go言語について発信します。

環境

OSバージョン:Windows11 Home 22H2
Go言語のバージョン:go version go1.19.4 windows/amd64

初めに

その5で、fmt.Scanを調べていたのですが、fmt.Scanfとfmt.Scanlnが抜けてましたので、 改めてfmt.Scan系を調べます。 kurumi-bio.hatenablog.com

fmt.Scan

func Scan(a ...any) (n int, err error)

Scanは、標準入力からテキストを読み込みます。 テキストは連続する空白で区切られて、連続する引数に格納されます。 改行は空白としてカウントされます。 戻り値は、正常にスキャンされたアイテム数で、 アイテム数(テキストの空白区切り)が、引数より少ない場合は、 errに理由が格納されます。

<サンプルコード>SprintSample.go

package main

import "fmt"
import "os"

func main() {
    var s1, s2 string
    n, e := fmt.Scan(&s1, &s2)
    if e != nil {
        fmt.Fprintf(os.Stderr, "Scan Error: %s\n", e)
    } else {
        fmt.Printf("n= %d / s1=[%s] / s2=[%s]\n", n, s1, s2)
    }
}

<実行結果1>

SprintSample.goは、二つの引数がありますので、
空白が一つ含まれたテキストを読み込んで表示することができます。

<実行結果2>

空白が2つで、3つに区切られるテキストを読み込んだ場合は、
エラーにならず、2つ目までが変数に格納されます。

<実行結果3>

改行も空白として扱われるので、文字を含む改行を2回行うと変数に格納されます。

<実行結果4>

意外でしたが空行はカウントされず。文字を含む行だけ変数に格納されます。

<実行結果5>

改行も空白行として扱われるため、引数より少ないテキストを渡す方法がわからなかったのですが、
Ctl+cでエラーを発生させることができました。
けど、無理やりだったからか、意味不明なエラーになってしまいました。

fmt.Scanf

func Scanf(format string, a ...any) (n int, err error)

Scanfは、Scanと基本は同じですが、テキストの書式を指定する必要があります。 さらに入力に改行を含める場合は、書式の改行と合わせる必要があります。

<サンプルコード>ScanfSample.go

package main

import "fmt"
import "os"

func main() {
    var s1, s2 string
    var i1 int

    n, e := fmt.Scanf("%s%d%s", &s1, &i1, &s2)
    if e != nil {
        fmt.Fprintf(os.Stderr, "Scanf Error: %s\n", e)
    } else {
        fmt.Printf("n= %d / s1=[%s] / i=[%d] / s2=[%s]\n", n, s1, i1, s2)
    }
}

<実行結果1>

ScanfSample.goは、3つの引数があり、文字列、数値、文字列の型が入力可能になっています。

<実行結果2>

試しに2つ目の区切りに数値ではなく、文字列を記載すると、
expected integerのエラーが発生しました。

<実行結果3>

試しに途中で改行すると、
fmt.Scanと異なり改行を空白として扱わないので、
unexpected newlineのエラーが発生しました。

では、書式に改行を入れて試してみます。

<サンプルコード>ScanfSample.go

package main

import "fmt"
import "os"

func main() {
    var s1, s2, s3, s4 string
    var i1, i2 int

    n, e := fmt.Scanf("%s %d %s\n%s %d %s", &s1, &i1, &s2, &s3, &i2, &s4)
    if e != nil {
        fmt.Fprintf(os.Stderr, "Scan Error: %s\n", e)
    } else {
        fmt.Printf("n= %d / s1=[%s] / i=[%d] / s2=[%s]\n", n, s1, i1, s2)
        fmt.Printf("/ s3=[%s] / i2=[%d] / s4=[%s]\n", s3, i2, s4)
    }
}

<実行結果4>

改行が入っても読み込まれました。

fmt.Scanln

func Scanln(a ...any) (n int, err error)

Scanfは、Scanと基本は同じですが、改行で読込を終了します。

<サンプルコード>ScanlnSample.go

package main

import "fmt"
import "os"

func main() {
    var s1, s2 string
    n, e := fmt.Scanln(&s1, &s2)

    if e != nil {
        fmt.Fprintf(os.Stderr, "Scanln Error: %s\n", e)
    } else {
        fmt.Printf("n= %d / s1=[%s] / s2=[%s]\n", n, s1, s2)
    }
}

<実行結果1>

空白を一つ入れたテキストを入力すると、問題なく読み込めます。

<実行結果2>

fmt.Scanと異なり、一つ目の区切りに改行を使うと。
unexpected newlineのエラーが発生しました。

<実行結果3>

区切り文字が多くてもunexpected newlineのエラーが発生しました。

終わりに

改行の扱い方が関数によって異なるので注意する必要があり、使い難い感じがしました。