Was ist eine Rune?


185

Was ist ein runein Go?

Ich habe gegoogelt, aber Golang sagt nur in einer Zeile: runeist ein Alias ​​fürint32 .

Aber wie kommt es, dass Ganzzahlen überall verwendet werden, wie wenn Fälle ausgetauscht werden?

Das Folgende ist ein Funktionsaustauschfall. Was ist das alles <=und -?

Und warum gibt switches keine Argumente?

&&sollte bedeuten und aber was ist das r <= 'z'?

func SwapRune(r rune) rune {
    switch {
    case 'a' <= r && r <= 'z':
        return r - 'a' + 'A'
    case 'A' <= r && r <= 'Z':
        return r - 'A' + 'a'
    default:
        return r
    }
}

Die meisten von ihnen stammen von http://play.golang.org/p/H6wjLZj6lW

func SwapCase(str string) string {
    return strings.Map(SwapRune, str)
}

Ich verstehe, dass dies eine Zuordnung ist rune, stringdamit die ausgetauschte Zeichenfolge zurückgegeben werden kann. Aber ich verstehe nicht, wie genau runeoder bytefunktioniert hier.


Nebenbemerkung: Dies tut nicht das, was jüngere Leser für das englische Wort "Café" und andere wünschen - geschweige denn für andere Sprachen. Go verfügt über Bibliotheken mit angemessener Unterstützung für tatsächlich nützliche Varianten dieser Art von Transformation.
RedGrittyBrick

2
Für den Fall, dass jemand wissen möchte, woher das Wort "Rune" kommt: en.wikipedia.org/wiki/Runic_(Unicode_block)
Matt Browne

Antworten:


146

Runenliterale sind nur 32-Bit-Ganzzahlwerte ( sie sind jedoch untypisierte Konstanten, sodass sich ihr Typ ändern kann ). Sie repräsentieren Unicode-Codepunkte. Zum Beispiel ist das Runenliteral 'a'tatsächlich die Zahl 97.

Daher ist Ihr Programm ziemlich gleichbedeutend mit:

package main

import "fmt"

func SwapRune(r rune) rune {
    switch {
    case 97 <= r && r <= 122:
        return r - 32
    case 65 <= r && r <= 90:
        return r + 32
    default:
        return r
    }
}

func main() {
    fmt.Println(SwapRune('a'))
}

Es sollte offensichtlich sein, wenn Sie sich die Unicode-Zuordnung ansehen, die mit ASCII in diesem Bereich identisch ist . Darüber hinaus ist 32 tatsächlich der Versatz zwischen dem Groß- und Kleinbuchstaben des Zeichens. Also durch Zugabe 32zu 'A', man bekommt , 'a'und umgekehrt.


12
Dies funktioniert offensichtlich nur für ASCII-Zeichen und nicht für akkreditierte Zeichen wie 'ä', geschweige denn für kompliziertere Fälle wie 'ı' (U + 0131). Go verfügt über spezielle Funktionen, die Kleinbuchstaben zugeordnet werden können, z unicode.ToLower(r rune) rune.
Topskip

2
Und um die richtige Antwort von @ topskip mit einer SwapCase-Funktion zu ergänzen, die für alle Codepunkte und nicht nur für az funktioniert:func SwapRune(r rune) rune { if unicode.IsUpper(r) { r = unicode.ToLower(r) } else { r = unicode.ToUpper(r) }; return r }
ANisus

22
Runen sind int32-Werte. Das ist die ganze Antwort. Sie sind nicht "abgebildet" .
thwd

@AlixAxel: Das Verhalten von SimpleFold ist im Wesentlichen dasselbe (es verwendet für die meisten Runen auch ToLower und ToUpper). Es gibt einige Fälle, in denen es sich unterscheidet, wie zum Beispiel: DZ-> Dz, Dz-> dz, dz-> DZ. Meine SwapRune würde stattdessen gehen: DZ-> dz, Dz-> DZ, dz-> DZ. Ich mag Ihren Vorschlag besser :)
ANisus

3
Runen ähneln also C-Zeichen?
Kenny Worden

52

Aus den Versionshinweisen von Go lang: http://golang.org/doc/go1#rune

Rune ist ein Typ. Es belegt 32 Bit und soll einen Unicode- CodePoint darstellen . Als Analogie hat der in 'ASCII' codierte englische Zeichensatz 128 Codepunkte. Somit kann in ein Byte (8bit) gepasst werden. Ausgehend von dieser (fehlerhaften) Annahme behandelte C Zeichen als "Bytes" charund "Zeichenfolgen" als "Folge von Zeichen" char*.

Aber rate mal was. Es gibt viele andere Symbole, die von Menschen erfunden wurden, außer den 'abcde ..'-Symbolen. Und es gibt so viele, dass wir 32 Bit benötigen, um sie zu codieren.

In Golang stringist a dann eine Folge von bytes. Da jedoch mehrere Bytes einen Runencodepunkt darstellen können, kann ein Zeichenfolgenwert auch Runen enthalten. Es kann also in a konvertiert []runewerden oder umgekehrt.

Das Unicode-Paket http://golang.org/pkg/unicode/ kann einen Vorgeschmack auf den Reichtum der Herausforderung geben.


6
Mit dem aktuellen Unicode 6.3 sind über 110.000 Symbole definiert. Dies erfordert eine mindestens 21-Bit-Darstellung jedes Codepunkts, sodass a runeähnlich ist int32und viele Bits enthält.
Rick-777

2
Sie sagen "a stringist eine Folge von runes" - ich glaube nicht, dass das stimmt? Go blog : "Ein String ist nur ein Bündel von Bytes"; Go lang spec : "Ein String-Wert ist eine (möglicherweise leere) Folge von Bytes"
Chris Martin

1
Ich bin immer noch verwirrt. Ist String also ein Array von Runen oder ein Array von Bytes? Sind sie austauschbar?
Gogofan

1
@prvn Das ist falsch. Es ist, als würde man sagen, ein Bild ist keine Folge von Bytes, sondern eine Folge von Pixeln. Aber tatsächlich ist es darunter eine Reihe von Bytes. Ein String ist eine Reihe von Bytes, keine Runen. Bitte lesen Sie die Spezifikation .
Inanc Gumus

1
@prvn Aber das kann man nicht sagen not bytes. Dann könnte man sagen: "Strings bestehen aus Runen und Runen aus Bytes" So etwas. Dann wieder. es ist nicht ganz wahr.
Inanc Gumus

27

Ich habe versucht, meine Sprache einfach zu halten, damit ein Laie versteht rune.

Eine Rune ist ein Charakter. Das ist es.

Es ist ein einzelnes Zeichen. Es ist ein Zeichen aus jedem Alphabet aus jeder Sprache von überall auf der Welt.

Um einen String zu bekommen, verwenden wir

double-quotes ""

ODER

back-ticks ``

Eine Zeichenfolge unterscheidet sich von einer Rune. In Runen verwenden wir

single-quotes ''

Jetzt ist eine Rune auch ein Alias ​​für int32... Äh was?

Der Grund, warum Rune ein Alias int32ist, liegt darin, dass wir dies bei Codierungsschemata wie den folgenden sehen Geben Sie hier die Bildbeschreibung ein

Jedes Zeichen ist einer Nummer zugeordnet, und daher ist es die Nummer, die wir speichern. Zum Beispiel ist eine Zuordnung zu 97 und wenn wir diese Nummer speichern, ist es nur die Nummer und so ist Rune ein Alias ​​für int32. Ist aber nicht irgendeine Zahl. Es ist eine Zahl mit 32 'Nullen und Einsen' oder '4' Bytes. (Hinweis: UTF-8 ist ein 4-Byte-Codierungsschema.)

Wie verhalten sich Runen zu Saiten?

Eine Zeichenfolge ist eine Sammlung von Runen. Im folgenden Code:

    package main

    import (
        "fmt"
    )

    func main() {
        fmt.Println([]byte("Hello"))
    }

Wir versuchen, einen String in einen Bytestrom umzuwandeln. Die Ausgabe ist:

[72 101 108 108 111]

Wir können sehen, dass jedes der Bytes, aus denen diese Zeichenfolge besteht, eine Rune ist.


2
A string is not a collection of runesDies ist streng genommen nicht korrekt. Stattdessen ist string ein Byte-Slice, das mit utf8 codiert ist. Jedes Zeichen in einer Zeichenfolge benötigt tatsächlich 1 bis 3 Bytes, während jede Rune 4 Bytes benötigt. Sie können zwischen String und [] Rune konvertieren, aber sie sind unterschiedlich.
Eric Wang

2
Rune ist kein Zeichen, eine Rune repräsentiert einen Unicode-Codepunkt. Und ein Codepunkt zeigt nicht unbedingt auf ein Zeichen.
Inanc Gumus

Es lohnt sich hinzuzufügen, dass "eine Rune auch ein Alias ​​für int32 ist", ja, aber das bedeutet nicht, dass es für die Komprimierung von Armen nützlich ist ... Wenn Sie so etwas wie 55296 treffen, geht die String-Konvertierung in die Irre: Go Playground
kubanczyk

26

Ich habe nicht genug Ruf, um einen Kommentar zur Antwort von fabrizioM zu schreiben , daher muss ich ihn stattdessen hier posten.

Fabrizios Antwort ist weitgehend richtig, und er hat sicherlich die Essenz des Problems erfasst - obwohl eine Unterscheidung getroffen werden muss.

Eine Zeichenfolge ist NICHT unbedingt eine Folge von Runen. Es ist ein Wrapper über einem 'Slice of Bytes', wobei ein Slice ein Wrapper über ein Go-Array ist. Welchen Unterschied macht das?

EIN Runentyp ist notwendigerweise ein 32-Bit-Wert, was bedeutet, dass eine Folge von Werten von Runentypen notwendigerweise eine bestimmte Anzahl von Bits x * 32 haben würde. Strings sind eine Folge von Bytes und haben stattdessen eine Länge von x * 8 Bits. Wenn alle Zeichenfolgen tatsächlich in Unicode wären, hätte dieser Unterschied keine Auswirkungen. Da Zeichenfolgen jedoch Slices von Bytes sind, kann Go ASCII oder eine beliebige andere Bytecodierung verwenden.

String-Literale müssen jedoch in die in UTF-8 codierte Quelle geschrieben werden.

Informationsquelle: http://blog.golang.org/strings


1
Guter Punkt ! Jede Rune benötigt 4 Bytes, aber jedes Zeichen in der Zeichenfolge wird mit utf8 codiert, also höchstens 1 ~ 3 Bytes.
Eric Wang

15

( Ich hatte das Gefühl, dass die obigen Antworten die Unterschiede und Beziehungen zwischen stringund immer noch nicht []runesehr deutlich darstellten, daher würde ich versuchen, eine weitere Antwort mit Beispiel hinzuzufügen.)

Wie @Strangeworkdie Antwort sagte, stringund []runesind ganz anders.

Unterschiede - string& []rune:

  • string valueist ein schreibgeschütztes Byte-Slice. Und ein Zeichenfolgenliteral ist in utf-8 codiert. Jedes Zeichen in stringtatsächlich nimmt 1 ~ 3 Bytes, während jeder runedauert 4 Bytes
  • Denn stringsowohl len()als auch der Index basieren auf Bytes.
  • Denn []runesowohl len()als auch der Index basieren auf Rune (oder Int32).

Beziehungen - string& []rune:

  • Wenn Sie von stringnach konvertieren , []runewird jedes utf-8-Zeichen in dieser Zeichenfolge zu a rune.
  • In ähnlicher Weise wird bei der umgekehrten Konvertierung bei der Konvertierung von []runenach stringjedes runeein utf-8-Zeichen in der string.

Tipps:

  • Sie können zwischen stringund konvertieren []rune, aber sie unterscheiden sich in Typ und Gesamtgröße.

(Ich würde ein Beispiel hinzufügen, um dies deutlicher zu zeigen.)


Code

string_rune_compare.go:

// string & rune compare,
package main

import "fmt"

// string & rune compare,
func stringAndRuneCompare() {
    // string,
    s := "hello你好"

    fmt.Printf("%s, type: %T, len: %d\n", s, s, len(s))
    fmt.Printf("s[%d]: %v, type: %T\n", 0, s[0], s[0])
    li := len(s) - 1 // last index,
    fmt.Printf("s[%d]: %v, type: %T\n\n", li, s[li], s[li])

    // []rune
    rs := []rune(s)
    fmt.Printf("%v, type: %T, len: %d\n", rs, rs, len(rs))
}

func main() {
    stringAndRuneCompare()
}

Ausführen:

Führen Sie string_rune_compare.go aus

Ausgabe:

hello你好, type: string, len: 11
s[0]: 104, type: uint8
s[10]: 189, type: uint8

[104 101 108 108 111 20320 22909], type: []int32, len: 7

Erläuterung:

  • Die Zeichenfolge hello你好hat die Länge 11, da die ersten 5 Zeichen jeweils nur 1 Byte benötigen, während die letzten 2 chinesischen Zeichen jeweils 3 Byte benötigen.

    • So, total bytes = 5 * 1 + 2 * 3 = 11
    • Da len()on string auf Bytes basiert, wird somit die erste Zeile gedrucktlen: 11
    • Da der Index für eine Zeichenfolge ebenfalls auf Bytes basiert, geben die folgenden 2 Zeilen Werte vom Typ aus uint8(da bytees sich um einen Alias-Typ von uint8, in go handelt).
  • Bei der Konvertierung stringnach []runewurden 7 utf8-Zeichen gefunden, also 7 Runen.

    • Da len()on []runeauf Rune basiert, wird somit die letzte Zeile gedruckt len: 7.
    • Wenn Sie []runeüber Index arbeiten, greift es auf Basis der Rune zu.
      Da jede Rune von einem utf8-Zeichen in der ursprünglichen Zeichenfolge stammt, können Sie auch sagen, dass beide len()und []runedie Indexoperation auf utf8-Zeichen basieren.

"Für Zeichenfolgen basieren sowohl len () als auch index auf Bytes." Könnten Sie das etwas näher erläutern? Wenn ich das mache fmt.Println("hello你好"[0]), wird der tatsächliche UTF-8-Codepunkt anstelle von Bytes zurückgegeben.
Julian

@Julian Bitte werfen Sie einen Blick auf die Ausgabe des Programms in der Antwort, denn s[0]es druckt s[0]: 104, type: uint8, der Typ ist uint8, bedeutet , es ist ein Byte. hVerwenden Sie für ASCII-Zeichen wie utf-8 auch ein einzelnes Byte, um es darzustellen, sodass der Codepunkt mit dem einzelnen Byte identisch ist. aber für chinesische Zeichen wie verwendet es 3 Bytes.
Eric Wang

7

Alle anderen haben den Teil im Zusammenhang mit Runen behandelt, deshalb werde ich nicht darüber sprechen.

Es gibt jedoch auch eine Frage, die sich darauf bezieht switch, keine Argumente zu haben. Dies liegt einfach daran, dass in Golang switchohne Ausdruck eine alternative Möglichkeit ist, die if / else-Logik auszudrücken. Schreiben Sie zum Beispiel Folgendes:

t := time.Now()
switch {
case t.Hour() < 12:
    fmt.Println("It's before noon")
default:
    fmt.Println("It's after noon")
}

ist dasselbe wie dies zu schreiben:

t := time.Now()
if t.Hour() < 12 {
    fmt.Println("It's before noon")
} else {
    fmt.Println("It's after noon")
}

Sie können mehr lesen hier .


0

Eine Rune ist ein int32-Wert und daher ein Go-Typ, der zur Darstellung eines Unicode-Codepunkts verwendet wird. Ein Unicode-Codepunkt oder eine Codeposition ist ein numerischer Wert, der normalerweise zur Darstellung einzelner Unicode-Zeichen verwendet wird.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.