buffo.Scanner逐行读取文件的奇怪行为

ABr*_*Bri 3 go

我使用bufio.Scanner逐行读取文件到变量wordlist([] [] byte)

这是代码(使用go 1.1/1.3进行测试).

package main

import (
    "bufio"
    "fmt"
    "log"
    "os"
)

func main() {
    fle, err := os.Open("words.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer fle.Close()

    scanner := bufio.NewScanner(fle)

    n := 1000
    dCnt := 5
    var wordlist [][]byte

    for scanner.Scan() {
        if len(wordlist) == n {
            break
        }
        word := scanner.Bytes()
        for ii := 0; ii < len(wordlist); ii++ {
            if string(word) == string(wordlist[ii]) {
                log.Println(ii, string(word), string(wordlist[ii]))
                log.Println(len(wordlist), "double")

                dCnt--
                if dCnt == 0 {
                    for i, v := range wordlist {
                        fmt.Println(i, string(v))
                    }
                    log.Fatal("double")
                }
            }
        }
        wordlist = append(wordlist, word)
    }
    if err := scanner.Err(); err != nil {
        log.Fatal(err)
    }
}
Run Code Online (Sandbox Code Playgroud)

words.txt是一个5040行序列"abcdefg"的排列文件:

line 1 .. 
abcdefg
abcdegf
abcdfeg
abcdfge
..
line 510 ..
afcdbge
afcdebg
afcdegb
afcdgbe
afcdgeb
.. line 5040
Run Code Online (Sandbox Code Playgroud)

由这个小python脚本生成:

from itertools import permutations as perm
c = "abcdefg"
p = perm(c, len(c))
with file('words.txt','wb') as outFle:
    for i in xrange(5040):
        n = ''.join(p.next())
        print >> outFle, n
Run Code Online (Sandbox Code Playgroud)

问题是,运行上面的程序后,wordlist包含以下内容:

索引字符串(wordlist [])

0 afcdebg      <-- this is line 513 of words.txt
1 afcdegb
2 afcdgbe
3 afcdgeb
...
510 bdefcag
511 bdefcga
512 afcdebg    <-- this is the begin of a repition of line 513 .. 1024 in words.ttx
513 afcdegb
514 afcdgbe 
Run Code Online (Sandbox Code Playgroud)

相反,wordlist应该包含前1000行words.txt

有任何想法吗 ?

答案由Daniel Darabos提供(见下文)

改变

字:= scanner.Bytes()

word:= scanner.Text()'完成了这项工作.

(谢谢你的帮助!)

Dan*_*bos 9

文件Scanner.Bytes说:

底层数组可能指向将被后续Scan扫描覆盖的数据.

因此,如果保存返回的切片,您可以期望看到其内容发生变化.这会对您的应用程序造成严重破坏.最好不要保存返回的切片!

一个很好的解决方案是从字节构建一个字符串:

word := string(scanner.Bytes())
Run Code Online (Sandbox Code Playgroud)

然后你可以在任何地方使用字符串,代码变得更加愉快.

到底是怎么回事?

为什么Scanner.Bytes恨我?答案也在文档中:

它没有分配.

这使得Scanner非常高效.从你看到的,我猜它在构造函数中为512行分配缓冲区,然后在它们上面旋转.

在不需要保留对行的引用的应用程序中,这不是问题.(例如,类似grep程序只查看每一行.)通常,您解析该行并存储对该行的引用.但是如果你想存储原始字节数据,你有责任从中复制出来Scanner.

这可能很麻烦,但是虽然您可以在不方便的行为之上实现方便的行为,但是在低效行为之上实现有效行为是不可能的.


还有一个用于生成输入的更简单的脚本:

import itertools
for p in itertools.permutations('abcdefg'):
  print ''.join(p)
Run Code Online (Sandbox Code Playgroud)