簡體   English   中英

GoLang在字節片的第N行獲取字符串

[英]GoLang Get String at Line N in 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

只處理問題部分(而不是理智的問題)-您有[]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

從這里開始,可以很容易地將其放回現有代碼中。

這是一個快速(以納秒為單位)隨機訪問文本行作為字節數據的示例。 數據被緩存並在內存中建立索引。

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
    }
}

產量

$ go test -bench=. lines.go lines_test.go
BenchmarkNewLines-8       1000   1898347 ns/op     1998898 B/op   2 allocs/op
BenchmarkLineAt-8     50000000        45.1 ns/op        49 B/op   0 allocs/op

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM