Fastest way to find the number of lines in go?

What i am using now:

numlines := strings.Count(editor.Text(), "\n")
fmt.Print(strconv.Itoa(numlines))
message.SetText(strconv.Itoa(numlines))

This is triggered whenever the text field is updated. What is the most suitable way to do this?

+4
source share
2 answers

It's fine. But do not forget that if the last character is not a newline character, you need to add 1 to the number of occurrences, as this will be the number of lines (the last line may not end with a new line).

We might think that since the substring you are counting is only one character (one rune), we could create our own solution that takes into account only the appearance of this single character (instead of counting the substrings). It might look like this:

func countRune(s string, r rune) int {
    count := 0
    for _, c := range s {
        if c == r {
            count++
        }
    }
    return count
}

(A for range string rune s.)

( Go Playground):

fmt.Println(countRune("asdf\nasdf\nasdf\n", '\n')) // Prints 3

, byte UTF-8 strings.Count() , 1:

// Count counts the number of non-overlapping instances of substr in s.
// If substr is an empty string, Count returns 1 + the number of Unicode code points in s.
func Count(s, substr string) int {
    if len(substr) == 1 && cpu.X86.HasPOPCNT {
        return countByte(s, byte(substr[0]))
    }
    return countGeneric(s, substr)
}

func countByte(s string, c byte) int // ../runtime/asm_amd64.s

( ), "" , Text(), .

+4

, <testing.

, lorem ipsum strings.Count for range string, byte string. , byte string, byte .

$ gotest lines_test.go -bench=.
data: /home/peter/shakespeare.pg100.txt 5589889
BenchmarkStringCount-4     30000000    57.3 ns/op     0 B/op   0 allocs/op
BenchmarkStringByRune-4     3000000   563 ns/op       0 B/op   0 allocs/op
BenchmarkBytesToString-4   10000000   173 ns/op     480 B/op   1 allocs/op
BenchmarkBytesCount-4      20000000    61.2 ns/op     0 B/op   0 allocs/op

lines_test.go:

package main

import (
    "bytes"
    "strconv"
    "strings"
    "testing"
)

func linesStringCount(s string) string {
    n := strings.Count(s, "\n")
    if len(s) > 0 && !strings.HasSuffix(s, "\n") {
        n++
    }
    return strconv.Itoa(n)
}

func linesStringByRune(s string) string {
    n := 0
    for _, r := range s {
        if r == '\n' {
            n++
        }
    }
    if len(s) > 0 && !strings.HasSuffix(s, "\n") {
        n++
    }
    return strconv.Itoa(n)
}

func linesBytesCount(s []byte) string {
    nl := []byte{'\n'}
    n := bytes.Count(s, nl)
    if len(s) > 0 && !bytes.HasSuffix(s, nl) {
        n++
    }
    return strconv.Itoa(n)
}

var data = []byte(`Lorem ipsum dolor sit amet, consectetur adipiscing elit, 
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 
Ut enim ad minim veniam, 
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 
Excepteur sint occaecat cupidatat non proident, 
sunt in culpa qui officia deserunt mollit anim id est laborum.`)

func BenchmarkStringCount(b *testing.B) {
    text := string(data)
    b.ReportAllocs()
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        _ = linesStringCount(text)
    }
}

func BenchmarkStringByRune(b *testing.B) {
    text := string(data)
    b.ReportAllocs()
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        _ = linesStringByRune(text)
    }
}

func BenchmarkBytesToText(b *testing.B) {
    b.ReportAllocs()
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        _ = string(data)
    }
}

func BenchmarkBytesCount(b *testing.B) {
    text := data
    b.ReportAllocs()
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        _ = linesBytesCount(text)
    }
}
+1

Source: https://habr.com/ru/post/1689122/


All Articles