37220


GO言語を使い、比較的大きなファイル（~100M）を読み込むときに、エラーが発生していまい困っておりました。

Webを参照にして、エラーが発生する箇所は自分で解決できましたが、  
なぜ違うfunctionを使うだけで、一方はエラーが出て、もう一方はスムーズにいくのかが理解できず、ここで質問させていただいています。お手数をおかけしますが、下記のプログラムで違いを説明していただけると勉強になり、大変ありがたいです。

[code]

    package main
    
    import (
          "io"
        　"os"
        　"fmt"
          "bufio"
    )
    
    func ReadLines01(path string) ([]string, error) {
      file, err := os.Open(path)
      if err != nil {
        return nil, err
      }
      defer file.Close()
    
      var lines []string
      scanner := bufio.NewScanner(file)
      for scanner.Scan() {
            lines = append(lines, scanner.Text())
      }
      return lines, scanner.Err()
    }
    
    
    func ReadLines02(path string) []string {
        file, _ := os.Open(path);
        defer file.Close()
    
        reader := bufio.NewReaderSize(file, 64*1024)
    
        var lines []string
        for {
            line_byte, _, err := reader.ReadLine()
            a_line := string(line_byte)
            lines = append(lines, a_line)
            if err == 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)) 
    }
    
[/code]

以前よりscannerを使った方法（ここではReadLine01）を使っており、これまではエラーも発生せず正常に動いていました。上記でも示しましたが、本日、~100Mサイズのテキストファイルを読み込むときに、エラーが発生していました。

具体的には、  
テキストファイルの内容は、数字＋文字の羅列です。  
横に最大約60000文字ほどあり、縦には3000行ほどです。  
（例：324.155969:286.733035 323.959871:286.938968 323.763772:287.144901）  
(申し訳ございません、実際にファイルをアップすればよいのですが、ここで、100Mのファイルをアップする方法がわかりませんでしたので、このような方法にしました。)

このファイルを読み込むときに、ReadLine01方式では、上から十数行で結果が返ってきてしまっていました。（例:
len(Data01)が12などになります。）

この問題のあるファイルは、別のscriptで作られたものです。  
しかし、不思議なことに、別に作成されたファイル（数字は違いますが、同じ内容）ではReadLine01でも完全に読むことができるものもありました。（このようなファイルを１００個ほど作成して、完全に読み込めるものは10個程度でした。）

webで解決方法を探していたところ、NewReaderSizeをみつけましたので、見よう見まねで、functionを作成（ReadLine02方式）してみたところ、上記の100ファイルすべてで、完全に読み込むことができるようになりました。

自分の理解が足りていないだけだと思いますが、  
ReadLine01方式のbufio.NewScannerでは、情報をscannerに保持して、それをScanすることで一行ずつ読んでいくと理解しています。また、ReadLine02方式のbufio.NewReaderSize(file,
64*1024)では、ある程度大きなbufferを用意しておいて、reader.ReadLine()でやはり一行ずつ読んでいくと理解しています。

これからだけですと、単純に原因は、Scannerには読み込むときの現在サイズが存在すると思ってしまいます。

質問ですが、  
（１）scannerではファイルを読み込むときの限界サイズはあるのでしょうか？  
（２）それとも、byteに変換するところが上記の問題の原因なのでしょうか？

自分の不勉強で、皆様の貴重なお時間を割いていただいて大変申し訳ありませんが、  
ご教授をお願いします。よろしくお願いいたします。

ちなみにGoのversionは、go1.8.1 linux/amd64です。


CC BY-SA 3.0
2017-08-08T18:37:21.780
37031
2017-08-15T09:43:20.077
11048
question
go
GO言語でファイルの読み込み方の違いについて


MaxScanTokenSize の話は既に書かれていますので、別解を。

bufio.Buffer#ReadLine は戻り値の2番目に、「行が長すぎて読み込み途中だったのかどうか」を返します。ですのでこれが false
であれば行末を読み込めたという事になります。つまりそこまで文字列を足すループを書けば良いです。

[code]

    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
    }
    
[/code]

ちなみに Scanner のデフォルト動作は CRLF の CR を取り除くので、もしこの動作が期待しない物ならば Scanner に渡す splitter
を自作するか、上記の様に prefix を判定しながら読む事になります。


CC BY-SA 3.0
2017-08-15T09:43:20.077
37220
2017-08-15T09:43:20.077
440
37031
answer