Edited at

bufio.Scannerのend-of-line判断を変更してみる

More than 3 years have passed since last update.

テキストファイルから1行ずつ取得して処理したい場合、

bufio.Scanner を使いますね。

bufio.Scanner には、Splitというメソッドがあり、

行末の判断基準を変更することができるようです。

デフォルトでは、LF(\n)または、CRLF(\r\n)を見つけると行末(end-of-line)だと判断して、

1行分のデータを返してくれます。

(CRLFが除かれたデータを取得できます)

http://golang.org/pkg/bufio/#ScanLines

改行が CR(\r) だけの場合には、正しく動作してくれません。

ということで、Scannerがデフォルトで使用している ScanLines を元にして、

チョイ足しで、改行が CR(\r)だけの場合に対応してみました。

まずは、type SplitFunc を実装する必要があります。

bufio の ScanLines を基にして、

3ステップ追加しただけですが、、、

func CustomScan(data []byte, atEOF bool) (advance int, token []byte, err error) {

if atEOF && len(data) == 0 {
return 0, nil, nil
}
var i int
if i = bytes.IndexByte(data, '\n'); i >= 0 {
// We have a full newline-terminated line.
return i + 1, dropCR(data[0:i]), nil
}
if i = bytes.IndexByte(data, '\r'); i >= 0 {
// ここを追加した。(CR があったら、そこまでのデータを返そう)
return i + 1, data[0:i], nil
}
// If we're at EOF, we have a final, non-terminated line. Return it.
if atEOF {
return len(data), dropCR(data), nil
}
// Request more data.
return 0, nil, nil
}

// dropCR drops a terminal \r from the data.
func dropCR(data []byte) []byte {
if len(data) > 0 && data[len(data)-1] == '\r' {
return data[0 : len(data)-1]
}
return data
}

このようなコードを自分のプログラムに追加して、

Splitメソッドに渡します。

func main() {

var fp *os.File
fp, _ = os.Open("./test.tsv")
defer fp.Close()
scanner := bufio.NewScanner(fp)
scanner.Split(CustomScan)
for scanner.Scan() {
// 1行ずつ処理する
....
}
}

それがどうした?って感じですが、

Atomエディタを使って、テキストファイルを作成したときに

改行が CR(\r) になってしまう現象に悩まされています。

Excel上のデータをコピーして、Atomにペースト(タブ区切り(TSV)の状態)すると発生します。

(解決策をご存知の方、教えてください。。。)

Atomエディタ側での解決策が見つからなかったので、bufioで何とかならんかとやってみた次第です。

もしかしたら、ReadString('\r') で対応してしまえばいいのかもしれませんが、

やっぱりCR,CRLFにも対応しておきたいし。

たいしたことはしていないですが、うまく動いたので良しです。