質問: io.EOF { break } else if err != nil { panic(err) } } return lines } func main () { fn := "/xxx/thumbnail-000.summary"; Data01, _ := ReadLines01(fn); //scannerを使った方法 Data02 := ReadLines02(fn) //ReadLineを使った方法 //Data01では初めの数行しか読み込まれませんでしたが、 //Data02では完全に読み込まれていました。 fmt.Println(len(Data01), len(Data02)) } ``` 以前よりscannerを使った方法(ここではReadLine01)を使っており、これまではエラーも発生せず正常に動いていました。上記でも示しましたが、本日、~100Mサイズのテキストファイルを読み込むときに、エラーが発生していました。 具体的には、 テキストファイルの内容は、数字+文字の羅列です。 横に最大約60000文字ほどあり、縦には3000行ほどです。 (例:324.155969:286.733035 323.959871:286.938968 323.763772:287.144901) (申し訳ございません、実際にファイルをアップすればよいのですが、ここで、100Mのファイルをアップする方法がわかりませんでしたので、このような方法にしました。) このファイルを読み込むときに、ReadLine01方式では、上から十数行で結果が返ってきてしまっていました。(例: len(Data01)が12などになります。) この問題のあるファイルは、別のscriptで作られたものです。 しかし、不思議なことに、別に作成されたファイル(数字は違いますが、同じ内容)ではReadLine01でも完全に読むことができるものもありました。(このようなファイルを100個ほど作成して、完全に読み込めるものは10個程度でした。) webで解決方法を探していたところ、NewReaderSizeをみつけましたので、見よう見まねで、functionを作成(ReadLine02方式)してみたところ、上記の100ファイルすべてで、完全に読み込むことができるようになりました。 自分の理解が足りていないだけだと思いますが、 ReadLine01方式のbufio.NewScannerでは、情報をscannerに保持して、それをScanすることで一行ずつ読んでいくと理解しています。また、ReadLine02方式のbufio.NewReaderSize(file, 64*1024)では、ある程度大きなbufferを用意しておいて、reader.ReadLine()でやはり一行ずつ読んでいくと理解しています。 これからだけですと、単純に原因は、Scannerには読み込むときの現在サイズが存在すると思ってしまいます。 質問ですが、 (1)scannerではファイルを読み込むときの限界サイズはあるのでしょうか? (2)それとも、byteに変換するところが上記の問題の原因なのでしょうか? 自分の不勉強で、皆様の貴重なお時間を割いていただいて大変申し訳ありませんが、 ご教授をお願いします。よろしくお願いいたします。 ちなみにGoのversionは、go1.8.1 linux/amd64です。
回答: MaxScanTokenSize の話は既に書かれていますので、別解を。 bufio.Buffer#ReadLine は戻り値の2番目に、「行が長すぎて読み込み途中だったのかどうか」を返します。ですのでこれが false であれば行末を読み込めたという事になります。つまりそこまで文字列を足すループを書けば良いです。 ``` func ReadLines02(path string) ([]string, error) { file, err := os.Open(path) if err != nil { return nil, err } defer file.Close() r := bufio.NewReader(file) var lines []string for { var err error var line string var b []byte prefix := true for prefix && err == nil { b, prefix, err = r.ReadLine() line += string(b) } if err == io.EOF { break } else if err != nil { return nil, err } lines = append(lines, line) } return lines, nil } ``` ちなみに Scanner のデフォルト動作は CRLF の CR を取り除くので、もしこの動作が期待しない物ならば Scanner に渡す splitter を自作するか、上記の様に prefix を判定しながら読む事になります。