GoLang在Byte Slice的N行获取字符串

时间:2017-10-26 10:53:22

标签: string go random byte slice

在个人项目中,我正在实现一个从长文件中返回随机行的函数。为了使它工作,我必须创建一个函数,在N行返回一个字符串,第二个函数创建一个介于0和文件行之间的随机数。在我实现这些时,我认为默认情况下将数据存储在字节切片中可能更有效,而不是将它们存储在必须在运行时读取的单独文件中。

问题:我如何实现一个函数,该函数在我文件的[]byte表示的随机行返回一个字符串。

我从文件中获取字符串的功能:

func atLine(n int) (s string) {
    f, err := os.Open("./path/to/file")
    if err != nil {
        panic("Could not read file.")
    }
    defer f.Close()
    r := bufio.NewReader(f)
    for i := 1; ; i++ {
        line, _, err := r.ReadLine()
        if err != nil {
            break
        }
        if i == n {
            s = string(line[:])
            break
        }
    }
    return s
}

其他信息:

  • 行数最多不超过50个字符
  • 行没有特殊字符(虽然欢迎处理这些字符的解决方案)
  • 文件中的行数是已知的,因此可以对[]byte
  • 应用相同的行数

2 个答案:

答案 0 :(得分:3)

只处理问题部分(而不是理智的部分) - 你有一个[]byte并且希望从中得到一个特定的字符串 - bytes.Reader没有ReadLine你已经注意到的方法。

您可以将字节阅读器传递给bufio.NewReader,并获得您尝试访问的ReadLine功能。

bytesReader := bytes.NewReader([]byte("test1\ntest2\ntest3\n"))
bufReader := bufio.NewReader(bytesReader)
value1, _, _ := bufReader.ReadLine()
value2, _, _ := bufReader.ReadLine()
value3, _, _ := bufReader.ReadLine()
fmt.Println(string(value1))
fmt.Println(string(value2))
fmt.Println(string(value3))

显然忽略错误是不明智的,但为了简洁起见,我在这里做。

https://play.golang.org/p/fRQUfmZQke

结果:

test1
test2
test3

从这里开始,您可以直接回到现有代码中。

答案 1 :(得分:0)

以下是快速(以纳秒为单位)随机访问文本行作为字节数据的示例。数据被缓冲并在内存中编入索引。

public class ItemDecorationAlbumColumns extends RecyclerView.ItemDecoration { private int space; public ItemDecorationAlbumColumns(int space) { this.space = space; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { if(parent.getChildAdapterPosition(view) % 2 == 0) { outRect.right = space; outRect.bottom = space; outRect.left = space/2; } else { outRect.left = space; outRect.right = space/2; outRect.bottom = space; } if(parent.getChildAdapterPosition(view) == 0) { outRect.top = space; outRect.left = space; } } }

lines.go

输出:

package main

import (
    "bytes"
    "fmt"
    "io/ioutil"
    "os"
)

type Lines struct {
    data  []byte
    index []int // line start, end pairs for data[start:end]
}

func NewLines(data []byte, nLines int) *Lines {
    bom := []byte{0xEF, 0xBB, 0xBF}
    if bytes.HasPrefix(data, bom) {
        data = data[len(bom):]
    }
    lines := Lines{data: data, index: make([]int, 0, 2*nLines)}
    for i := 0; ; {
        j := bytes.IndexByte(lines.data[i:], '\n')
        if j < 0 {
            if len(lines.data[i:]) > 0 {
                lines.index = append(lines.index, i)
                lines.index = append(lines.index, len(lines.data))
            }
            break
        }
        lines.index = append(lines.index, i)
        j += i
        i = j + 1
        if j > 0 && lines.data[j-1] == '\r' {
            j--
        }
        lines.index = append(lines.index, j)
    }
    if len(lines.index) != cap(lines.index) {
        lines.index = append([]int(nil), lines.index...)
    }
    return &lines
}

func (l *Lines) N() int {
    return len(l.index) / 2
}

func (l *Lines) At(n int) (string, error) {
    if 1 > n || n > l.N() {
        err := fmt.Errorf(
            "data has %d lines: at %d out of range",
            l.N(), n,
        )
        return "", err
    }
    m := 2 * (n - 1)
    return string(l.data[l.index[m]:l.index[m+1]]), nil
}

var (
    // The Complete Works of William Shakespeare
    // http://www.gutenberg.org/cache/epub/100/pg100.txt
    fName  = `/home/peter/shakespeare.pg100.txt`
    nLines = 124787
)

func main() {
    data, err := ioutil.ReadFile(fName)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        return
    }

    lines := NewLines(data, nLines)

    for _, at := range []int{1 - 1, 1, 2, 12, 42, 124754, lines.N(), lines.N() + 1} {
        line, err := lines.At(at)
        if err != nil {
            fmt.Fprintf(os.Stderr, "%d\t%v\n", at, err)
            continue
        }
        fmt.Printf("%d\t%q\n", at, line)
    }
}

0 data has 124787 lines: at 0 out of range 1 "The Project Gutenberg EBook of The Complete Works of William Shakespeare, by" 2 "William Shakespeare" 12 "Title: The Complete Works of William Shakespeare" 42 "SHAKESPEARE IS COPYRIGHT 1990-1993 BY WORLD LIBRARY, INC., AND IS" 124754 "http://www.gutenberg.org" 124787 "*** END: FULL LICENSE ***" 124788 data has 124787 lines: at 124788 out of range

lines_test.go

输出

package main

import (
    "io/ioutil"
    "math/rand"
    "testing"
)

func benchData(b *testing.B) []byte {
    data, err := ioutil.ReadFile(fName)
    if err != nil {
        b.Fatal(err)
    }
    return data
}

func BenchmarkNewLines(b *testing.B) {
    data := benchData(b)
    b.ReportAllocs()
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        lines := NewLines(data, nLines)
        _ = lines
    }
}

func BenchmarkLineAt(b *testing.B) {
    data := benchData(b)
    lines := NewLines(data, nLines)
    ats := make([]int, 4*1024)
    ats[0], ats[1] = 1, lines.N()
    rand.Seed(42)
    for i := range ats[2:] {
        ats[2+i] = 1 + rand.Intn(lines.N())
    }
    b.ReportAllocs()
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        at := ats[i%len(ats)]
        line, err := lines.At(at)
        if err != nil {
            b.Error(err)
        }
        _ = line
    }
}