2017-11-11 169 views
4

我现在使用什么:找到行数的最快方法?

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

这是每当一个文本框更新运行。最类似这样做的方式是什么?

回答

4

这很好。但是请不要忘记,如果最后一个字符不是换行符,则必须将出现次数加1,因为这将是行数(最后一行可能不会以换行符结束)。

我们可能会认为,由于您要计算的子字符串只是一个字符(单个rune),因此我们可以创建一个自定义解决方案,仅计算此单个字符的出现(而不是计算子字符串)。它可能看起来像这样:

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

(一种for rangestring值迭代其rune S)

并对其进行测试(试试在Go Playground):

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

在实践中,这将不会更快地计算换行符,因为这是UTF-8编码中的单个byte,并且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()方法,它创建并返回其内容的副本。

1

当您询问有关最快方法的问题时,您应该使用Go testing软件包基准设施进行测量。

例如,计数行数使用strings.Count并使用for range环在stringlorem ipsum中,并测量来自一个byte切片转换为string的任何额外费用。通过计算byte切片中的行数,您可以避免任何byte切片到string开销。

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