3 回答
TA贡献1798条经验 获得超7个赞
最后编辑
这是对花费微不足道的时间的问题的“逐行”解决方案,它会打印整个匹配行。
package main
import (
"bytes"
"fmt"
"io/ioutil"
)
func main() {
dat, _ := ioutil.ReadFile("./jumble.txt")
i := bytes.Index(dat, []byte("Iforgotmypassword"))
if i != -1 {
var x int
var y int
for x = i; x > 0; x-- {
if dat[x] == byte('\n') {
break
}
}
for y = i; y < len(dat); y++ {
if dat[y] == byte('\n') {
break
}
}
fmt.Println(string(dat[x : y+1]))
}
}
real 0m0.421s
user 0m0.068s
sys 0m0.352s
原始答案
如果您只需要查看字符串是否在文件中,为什么不使用正则表达式呢?
注意:我将数据保存为字节数组而不是转换为字符串。
package main
import (
"fmt"
"io/ioutil"
"regexp"
)
var regex = regexp.MustCompile(`Ilostmypassword`)
func main() {
dat, _ := ioutil.ReadFile("./jumble.txt")
if regex.Match(dat) {
fmt.Println("Yes")
}
}
jumble.txt是一个 859 MB 的包含换行符的混乱文本。
运行time ./code我得到:
real 0m0.405s
user 0m0.064s
sys 0m0.340s
为了尝试回答您的评论,我不认为瓶颈本质上来自于逐行搜索,Golang 使用一种有效的算法来搜索字符串/符文。
我认为瓶颈来自IO读取,当程序从文件读取时,它通常不会在读取队列中排在第一位,因此,程序必须等到可以读取才能开始实际比较。因此,当您一遍又一遍地阅读时,您将被迫等待轮到您的 IO。
给你一些数学,如果你的缓冲区大小是 64 * 1024(或 65535 字节),你的文件是 1 GB。将 1 GB / 65535 字节除以检查整个文件所需的 15249 次读取。在我的方法中,我“一次”读取整个文件并检查构造的数组。
我能想到的另一件事就是遍历文件所需的循环总数以及每个循环所需的时间:
给定以下代码:
dat, _ := ioutil.ReadFile("./jumble.txt")
sdat := bytes.Split(dat, []byte{'\n'})
for _, l := range sdat {
if bytes.Equal([]byte("Iforgotmypassword"), l) {
fmt.Println("Yes")
}
}
我计算出每个循环平均需要 32 纳秒,字符串 Iforgotmypassword 在我的文件中的第 100000000 行,因此这个循环的执行时间大约是 32 纳秒 * 100000000 ~= 3.2 秒。
TA贡献1853条经验 获得超9个赞
H. Ross's answer is awesome,但它会将整个文件读入内存,如果你的文件太大,这可能不可行。如果您仍然想逐行扫描,也许如果您正在搜索多个项目,我发现使用 scanner.Bytes() 而不是 scanner.Text() 可以稍微提高我机器上的速度,从 2.244s 到原题,1.608s。bufio 的 scanner.Bytes() 方法不分配任何额外的内存,而 Text() 从其缓冲区创建一个字符串。
package main
import (
"bufio"
"fmt"
"os"
"bytes"
)
// uses scanner.Bytes to avoid allocation.
func main() {
f, err := os.Open("./crackstation-human-only.txt")
scanner := bufio.NewScanner(f)
if err != nil {
panic(err)
}
defer f.Close()
toFind := []byte("Iforgotmypassword")
for scanner.Scan() {
if bytes.Contains(scanner.Bytes(), toFind) {
fmt.Println(scanner.Text())
}
}
}
TA贡献1719条经验 获得超6个赞
使用我自己的 700MB 测试文件和你的原始文件,时间刚刚超过 7 秒
使用 grep 是 0.49 秒
使用这个程序(不打印出行,它只是说是)0.082 秒
package main
import (
"bytes"
"fmt"
"io/ioutil"
"os"
)
func check(e error) {
if e != nil {
panic(e)
}
}
func main() {
find := []byte(os.Args[1])
dat, err := ioutil.ReadFile("crackstation-human-only.txt")
check(err)
if bytes.Contains(dat, find) {
fmt.Print("yes")
}
}
- 3 回答
- 0 关注
- 208 浏览
添加回答
举报