2014-07-23 18 views
5

Ich immer scheinen Zeichenfolgen in [] Byte zu Zeichenfolge wieder und wieder zu konvertieren. Ist da viel Aufwand? Gibt es einen besseren Weg?Go: Overhead der Konvertierung von [] Byte in String und umgekehrt

Zum Beispiel, hier ist eine Funktion, die einen UTF8-String akzeptiert, normalisiert es, Akzente entfernen, wandelt dann Sonderzeichen ASCII-Äquivalent:

var transliterations = map[rune]string{'Æ':"AE",'Ð':"D",'Ł':"L",'Ø':"OE",'Þ':"Th",'ß':"ss",'æ':"ae",'ð':"d",'ł':"l",'ø':"oe",'þ':"th",'Œ':"OE",'œ':"oe"} 
func RemoveAccents(s string) string { 
    b := make([]byte, len(s)) 
    t := transform.Chain(norm.NFD, transform.RemoveFunc(isMn), norm.NFC) 
    _, _, e := t.Transform(b, []byte(s), true) 
    if e != nil { panic(e) } 
    r := string(b) 

    var f bytes.Buffer 
    for _, c := range r { 
     temp := rune(c) 
     if val, ok := transliterations[temp]; ok { 
      f.WriteString(val) 
     } else { 
      f.WriteRune(temp) 
     } 
    } 
    return f.String() 
} 

mit einem String So beginne ich, weil das ist, was ich get, dann wandle ich es in ein Byte-Array um, dann zurück zu einem String, dann wieder zu einem Byte-Array und dann wieder zu einem String. Sicher ist das unnötig, aber ich kann nicht herausfinden, wie man das nicht macht ..? Und hat es wirklich viel Overhead oder muss ich mir keine Sorgen machen, dass die Dinge durch übermäßige Conversions verlangsamt werden?

(Auch wenn jemand die Zeit habe ich noch nicht herausgefunden, wie bytes.Buffer tatsächlich funktioniert, wäre es nicht besser, einen Puffer von 2x die Größe der Zeichenfolge zu initialisieren, die die maximale Ausgabegröße des Rückgabewerts ist ?)

Antwort

3

In Go, string s sind unveränderlich, so dass jede Änderung eine neue Zeichenfolge erstellt. Als allgemeine Regel konvertieren Sie einmal von einer string zu einer byte oder rune Scheibe und konvertieren Sie sie zurück in eine string. Um Neuzuweisungen für kleine und vorübergehende Zuordnungen zu vermeiden, sollten Sie zu viel Reserve zuweisen, wenn Sie die genaue Anzahl nicht kennen.

Zum Beispiel

package main 

import (
    "bytes" 
    "fmt" 
    "unicode" 
    "unicode/utf8" 

    "code.google.com/p/go.text/transform" 
    "code.google.com/p/go.text/unicode/norm" 
) 

var isMn = func(r rune) bool { 
    return unicode.Is(unicode.Mn, r) // Mn: nonspacing marks 
} 

var transliterations = map[rune]string{ 
    'Æ': "AE", 'Ð': "D", 'Ł': "L", 'Ø': "OE", 'Þ': "Th", 
    'ß': "ss", 'æ': "ae", 'ð': "d", 'ł': "l", 'ø': "oe", 
    'þ': "th", 'Œ': "OE", 'œ': "oe", 
} 

func RemoveAccents(b []byte) ([]byte, error) { 
    mnBuf := make([]byte, len(b)*125/100) 
    t := transform.Chain(norm.NFD, transform.RemoveFunc(isMn), norm.NFC) 
    n, _, err := t.Transform(mnBuf, b, true) 
    if err != nil { 
     return nil, err 
    } 
    mnBuf = mnBuf[:n] 
    tlBuf := bytes.NewBuffer(make([]byte, 0, len(mnBuf)*125/100)) 
    for i, w := 0, 0; i < len(mnBuf); i += w { 
     r, width := utf8.DecodeRune(mnBuf[i:]) 
     if s, ok := transliterations[r]; ok { 
      tlBuf.WriteString(s) 
     } else { 
      tlBuf.WriteRune(r) 
     } 
     w = width 
    } 
    return tlBuf.Bytes(), nil 
} 

func main() { 
    in := "test stringß" 
    fmt.Println(in) 
    inBytes := []byte(in) 
    outBytes, err := RemoveAccents(inBytes) 
    if err != nil { 
     fmt.Println(err) 
    } 
    out := string(outBytes) 
    fmt.Println(out) 
} 

Ausgang:

test stringß 
test stringss 
+0

Fancy. Die 'Breite' scheint unnötig zu sein ... kann ich nicht einfach' r, w: = utf8.DecodeRune' machen und dann die 'w = width' weglassen? – Alasdair

+0

@Alasdair: Nein. 'I, w: = 0, 0' erklärt' w' für den äußeren Bereich. 'r, w: = utf8.DecodeRune' würde' w' für den inneren Bereich redeclare. "i + = w" verwendet das "w" im äußeren Bereich, so dass Sie einen Fehler "' w deklariert und nicht verwendet "für das" w "im inneren Bereich erhalten. [Die Programmiersprache Go] (http://golang.org/ref/spec); [Blöcke] (http://golang.org/ref/spec#Blocks); [Deklarationen und Geltungsbereich] (http://golang.org/ref/spec#Declarations_and_scope); [Kurze Variablendeklarationen] (http://golang.org/ref/spec#Short_variable_declarations). – peterSO

+0

In Ordnung dann. Danke für die Erklärung. – Alasdair

1

Es gibt einen kleinen Overhead mit der Umwandlung einer Zeichenfolge in ein Byte Slice (kein Array, das ist ein different type). Nämlich das Reservieren des Speicherplatzes für das Byte-Slice.

Strings sind ein eigener Typ und sind eine Interpretation einer Bytefolge. Aber nicht jede Bytefolge ist eine nützliche Zeichenfolge. Strings sind auch immutable. Wenn Sie sich die strings package anschauen, werden Sie sehen, dass Strings sliced viel werden.

In Ihrem Beispiel können Sie die zweite Umwandlung zurück in String weglassen. Sie können auch über ein Byte-Slice reichen.

Wie bei jeder Frage zur Leistung: Sie müssen wahrscheinlich messen. Ist die Zuordnung von Byte-Slices wirklich dein Flaschenhals?

f := bytes.NewBuffer(make([]byte, 0, len(s)*2)) 

, wo Sie die Größe Ihrer Zeichenfolge eine Größe von 0 und eine Kapazität von 2x haben:

Sie können Ihre bytes.Buffer wie so initialisieren. Wenn Sie die Größe Ihres Puffers schätzen können, ist es wahrscheinlich gut, das zu tun. Es wird Ihnen ein paar Neuzuweisungen der zugrunde liegenden Byte-Slices sparen.

+0

Danke für die Hinweise auf den Puffer zu initialisieren. Für den Bereich über der Saite denke ich, dass es sich um eine Saite handeln muss, da die Saitenbereiche nach Rune reichen, was ich in diesem Fall brauche. Es könnte wahrscheinlich mit einem Scanner im Byte-Array gemacht werden, aber ich habe noch nicht gelernt, wie das geht. – Alasdair

+0

@Alasdair, können Sie Runen von einem '[] Byte mit' bufio.Reader.ReadRune' erhalten, oder direkter mit nur 'utf8.DecodeRune'. – JimB

2

Es gibt keine Antwort auf diese Frage. Wenn diese Conversions einen Leistungsengpass in Ihrer Anwendung darstellen, sollten Sie sie beheben. Wenn nicht: Nicht.

Haben Sie Ihre Anwendung unter realistischer Last profiliert und RemoveAccents ist der Engpass? Nein? Wieso sich die Mühe machen?

Wirklich: Ich nehme an, man könnte besser (im Sinne von weniger Müll, weniger Iterationen und weniger Konvertierungen), z. durch Verketten in einem "TransliterationTransformer". Aber ich bezweifle, dass es die Mühe machen würde.

+2

Nun ... Ich verstehe, was Sie sagen, aber ich habe eine Idee in meinem Kopf über die Effizienz meiner Codierung Stil. Es ist eine Frage der Gewohnheit, sich auf bestimmte Anwendungen zu konzentrieren anstatt sie zu profilieren und zu optimieren. – Alasdair

Verwandte Themen