kurumi-bioの雑記帳

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

初心者のGo言語 その8 <fmt.FscanとFscanf、Fscanln>

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

環境

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

初めに

今回も、fmtパッケージの機能を調査します。

fmt.Fscan

func Fscan(r io.Reader, a ...any) (n int, err error)

io.Readerからテキストを読み込み引数に格納する機能を提供します。
テキストはスペースで区切って、連続した引数に格納します。
改行はスペースとして扱います。
戻り値は、正常に読み込んだアイテム数とエラー内容です。

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

package main

import "fmt"
import "os"
import "io"

func main() {
    f, e := os.OpenFile("Fscan.txt", os.O_RDONLY, 0666)
    if e != nil {
        fmt.Fprintf(os.Stderr, "Open Error: %v\n", e)
    }

    var a, b string
    for {
        n, e2 := fmt.Fscan(f, &a, &b)
        if e2 == io.EOF {
            break
        }
        if e2 != nil {
            fmt.Fprintf(os.Stderr, "Fscan Error: %v\n", e2)
        } else {
            fmt.Printf("%d: %v %v\n", n, a, b)
        }
    }
    f.Close()

}

<読み込むファイル>Fscan.txt

あ い う え お
か き く け こ
さ
し
す

<実行結果>

"あいうえお"は、半角スペースで"かきくけこ"は、全角スペースを間に入れていますが、 両方とも区切り文字として認識されています。 "さ"と"し"の改行も区切り文字と認識されて、2つの引数に格納されていることがわかります。 "す"は、2つの引数を指定しているのに、1つだけだったので格納されなかったと思われます。

fmt.Fscanf

func Fscanf(r io.Reader, format string, a ...any) (n int, err error)

io.Readerからテキストを読み込み引数に格納する機能を提供します。
テキストはスペースで区切って、書式設定の形式で連続した引数に格納します。
改行は書式設定で改行(\n)を指定する必要があります。
戻り値は、正常に読み込んだアイテム数とエラー内容です。

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

package main

import "fmt"
import "os"
import "io"

func main() {
    f, e := os.OpenFile("Fscan.txt", os.O_RDONLY, 0666)
    if e != nil {
        fmt.Fprintf(os.Stderr, "Open Error: %v\n", e)
    }

    var s1, s2 string
    for {
        n, e2 := fmt.Fscanf(f, "%s %s", &s1, &s2)
        if e2 == io.EOF {
            break
        }
        if e2 != nil {
            fmt.Fprintf(os.Stderr, "Fscanf Error: %v\n", e2)
        } else {
            fmt.Printf("%d: %v %v\n", n, s1, s2)
        }
    }
    f.Close()

}

<実行結果>

fscanと同じファイル(Fscan.txt)を読み込ませたら"unexpected newline"のエラーが発生しました。
fmt.Fscanfの書式設定に改行(\n)が含まれていないため発生しています。
ですので、
n, e2 := fmt.Fscanf(f, "%s %s", &s1, &s2)
n, e2 := fmt.Fscanf(f, "%s %s\n", &s1, &s2)
変更して試してみます。

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

package main

import "fmt"
import "os"
import "io"

func main() {
    f, e := os.OpenFile("Fscan.txt", os.O_RDONLY, 0666)
    if e != nil {
        fmt.Fprintf(os.Stderr, "Open Error: %v\n", e)
    }

    var s1, s2 string
    for {
        n, e2 := fmt.Fscanf(f, "%s %s\n", &s1, &s2)
        if e2 == io.EOF {
            break
        }
        if e2 != nil {
            fmt.Fprintf(os.Stderr, "Fscanf Error: %v\n", e2)
        } else {
            fmt.Printf("%d: %v %v\n", n, s1, s2)
        }
    }
    f.Close()

}

<実行結果>

再度、同じファイル(Fscan.txt)を読み込ませたら"newline in format does not match input"のエラーが発生しました。
エラーの通りで、書式に[文字列 文字列 改行]を指定しているので、"あ い う"が書式に該当せずエラーになり、
"え お 改行"が、書式に該当し配列に格納されています。 では、読み込むファイルを[文字列 文字列 改行]で構成して試してみると

<読み込むファイル>Fscan.txt

あ い
う え
お か
き く
け こ
さ し

<実行結果>

エラーを発生せずに、ファイルの最後まで読み込むことができました。

fmt.Fscanln

func Fscanln(r io.Reader, a ...any) (n int, err error)

io.Readerからテキストを読み込み引数に格納する機能を提供します。
Fscanと同じですが、改行まで読み込みます。
最後の項目の後には、改行またはEOFが必要です。

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

package main

import "fmt"
import "os"
import "io"

func main() {
    f, e := os.OpenFile("Fscan.txt", os.O_RDONLY, 0666)
    if e != nil {
        fmt.Fprintf(os.Stderr, "Open Error: %v\n", e)
    }

    var s1, s2 string
    for {
        n, e2 := fmt.Fscanln(f, &s1, &s2)
        if e2 == io.EOF {
            break
        }
        if e2 != nil {
            fmt.Fprintf(os.Stderr, "Fscan Error: %v\n", e2)
        } else {
            fmt.Printf("%d: %v %v\n", n, s1, s2)
        }
    }
    f.Close()

}

<実行結果>

先ほどのファイル(ファイルを[文字列 文字列 改行]で構成したファイル)を読み込ませたら正常に動作しました。
書式設定ができないけど、\nが不要なfmt.Fscanfという感じです。

サンプルプログラム

東北地方の県庁所在地と人口が記載されたテキストファイルを読み込み
標準出力に出力するプログラムです。

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

package main

import "fmt"
import "os"
import "io"

func main() {
    f, e := os.OpenFile("touhoku.txt", os.O_RDONLY, 0666)
    if e != nil {
        fmt.Fprintf(os.Stderr, "Open Error: %v\n", e)
    }

    var s1, s2 string
    var i int
    for {
        _, e = fmt.Fscanf(f, "%s %s %d \n", &s1, &s2, &i)
        if e == io.EOF {
            break
        }
        if e != nil {
            fmt.Fprintf(os.Stderr, "Fscanf Error: %v\n", e)
        } else {
            fmt.Printf("県名=%s / 県庁所在地=%s / 人口=%d人\n", s1, s2, i)
        }
    }
    f.Close()

}

<読み込むファイル>touhoku.txt

青森県 青森市 1202030
岩手県 盛岡市 1177938
宮城県 仙台市 2277527
秋田県 秋田市 927561
山形県 山形市 1038788
福島県 福島市 1787126

<実行結果>

終わりに

空白区切りが使いにくい感じがします。
カンマまたはタブ区切りの方が一般的かなという感じがします。