2017-11-11 168 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) 
    } 
}