Runtime: Einführung in System.Rune

Erstellt am 16. Sept. 2017  ·  106Kommentare  ·  Quelle: dotnet/runtime

Angeregt durch die Diskussion hier:

https://github.com/dotnet/corefxlab/issues/1751

Eine der Herausforderungen, denen .NET mit seiner Unicode-Unterstützung gegenübersteht, besteht darin, dass es auf einem Design basiert, das heutzutage veraltet ist. Die Art und Weise, wie wir Zeichen in .NET darstellen, ist mit System.Char , was ein 16-Bit-Wert ist, der nicht ausreicht, um Unicode-Werte darzustellen.

.NET-Entwickler müssen etwas über die geheimnisvollen Ersatzpaare lernen:

https://msdn.microsoft.com/en-us/library/xcwwfbb8 (v=vs.110).aspx

Entwickler nutzen diese Unterstützung selten, hauptsächlich weil sie nicht genug mit Unicode vertraut sind, geschweige denn, was .NET ihnen zu bieten hat.

Ich schlage vor, dass wir ein System.Rune einführen, das von einer 32-Bit-Ganzzahl unterstützt wird und einem CodePoint entspricht, und dass wir in C# den entsprechenden rune -Typ als Alias ​​für diesen Typ auftauchen lassen.

rune würde der bevorzugte Ersatz für char werden und als Grundlage für die ordnungsgemäße Handhabung von Unicode und Zeichenfolgen in .NET dienen.

Warum der Name Rune, die Inspiration kommt von Go:

https://blog.golang.org/strings

Der Abschnitt "Codepunkte, Zeichen und Runen" liefert die Erklärung, eine Kurzversion ist:

„Codepunkt“ ist ein bisschen wie ein Schluck, also führt Go einen kürzeren Begriff für das Konzept ein: Rune. Der Begriff taucht in den Bibliotheken und im Quellcode auf und bedeutet genau dasselbe wie „Codepunkt“, mit einem interessanten Zusatz.

Update Ich habe jetzt eine Implementierung von System.Rune hier:

https://github.com/migueldeicaza/NStack/blob/master/NStack/unicode/Rune.cs

Mit folgender API:

public struct Rune {

    public Rune (uint rune);
    public Rune (char ch);

    public static ValueTuple<Rune,int> DecodeLastRune (byte [] buffer, int end);
    public static ValueTuple<Rune,int> DecodeLastRune (NStack.ustring str, int end);
    public static ValueTuple<Rune,int> DecodeRune (byte [] buffer, int start, int n);
    public static ValueTuple<Rune,int> DecodeRune (NStack.ustring str, int start, int n);
    public static int EncodeRune (Rune rune, byte [] dest, int offset);
    public static bool FullRune (byte [] p);
    public static bool FullRune (NStack.ustring str);
    public static int InvalidIndex (byte [] buffer);
    public static int InvalidIndex (NStack.ustring str);
    public static bool IsControl (Rune rune);
    public static bool IsDigit (Rune rune);
    public static bool IsGraphic (Rune rune);
    public static bool IsLetter (Rune rune);
    public static bool IsLower (Rune rune);
    public static bool IsMark (Rune rune);
    public static bool IsNumber (Rune rune);
    public static bool IsPrint (Rune rune);
    public static bool IsPunctuation (Rune rune);
    public static bool IsSpace (Rune rune);
    public static bool IsSymbol (Rune rune);
    public static bool IsTitle (Rune rune);
    public static bool IsUpper (Rune rune);
    public static int RuneCount (byte [] buffer, int offset, int count);
    public static int RuneCount (NStack.ustring str);
    public static int RuneLen (Rune rune);
    public static Rune SimpleFold (Rune rune);
    public static Rune To (Case toCase, Rune rune);
    public static Rune ToLower (Rune rune);
    public static Rune ToTitle (Rune rune);
    public static Rune ToUpper (Rune rune);
    public static bool Valid (byte [] buffer);
    public static bool Valid (NStack.ustring str);
    public static bool ValidRune (Rune rune);
    public override bool Equals (object obj);

    [System.Runtime.ConstrainedExecution.ReliabilityContractAttribute((System.Runtime.ConstrainedExecution.Consistency)3, (System.Runtime.ConstrainedExecution.Cer)2)]
    protected virtual void Finalize ();
    public override int GetHashCode ();
    public Type GetType ();
    protected object MemberwiseClone ();
    public override string ToString ();

    public static implicit operator uint (Rune rune);
    public static implicit operator Rune (char ch);
    public static implicit operator Rune (uint value);

    public bool IsValid {
        get;
    }

    public static Rune Error;
    public static Rune MaxRune;
    public const byte RuneSelf = 128;
    public static Rune ReplacementChar;
    public const int Utf8Max = 4;

    public enum Case {
        Upper,
        Lower,
        Title
    }
}

Bekannte Probleme aktualisieren

  • [x] Einige APIs oben nehmen eine uint, müssen eine Rune nehmen.
  • [ ] IComparable-Familie muss implementiert werden
  • [ ] RuneCount/RuneLen brauchen bessere Namen, siehe Dokumentation (sie sollten vielleicht Utf8BytesNeeded sein?)
  • [ ] Oben verweisen die "usstring"-APIs auf meine UTF8-API, dies ist wirklich kein Teil der API, aber wir sollten überlegen, ob es in einigen von ihnen ein Gateway zu System.String oder zu Utf8String gibt.
api-needs-work area-System.Runtime up-for-grabs

Hilfreichster Kommentar

Ich habe es in der ursprünglichen Ausgabe gesagt und werde es noch einmal sagen. Aufzugeben, was ein Standard sagt, weil Sie den Ausdruck nicht mögen, wird mehr verwirren als lösen, und angesichts der Tatsache, dass es eine Runencodepage in Unicode gibt, verwirrt das nur noch mehr.

Der Name ist falsch.

Alle 106 Kommentare

Erwarten Sie, dass die In-Memory-Darstellung Zeichenfolgen von 32-Bit-Objekten sind oder spontan übersetzt werden? Was ist mit der Gedächtnisverdopplung, wenn ersteres? Wie wirkt sich letzteres auf die Leistung aus?

Ist es eine gute Idee, eine Unicode-bezogene Technologie nach einem bestimmten Unicode-unterstützten Skript (und eine Technologie zur Verbesserung der Unterstützung der Astralebene nach einem BMP-Skript) zu benennen?

Ich denke, der Vorschlag (und vielleicht muss er expliziter gemacht werden) ist, dass sich die In-Memory-Darstellung von Strings überhaupt nicht ändert. Der Typ Rune stellt lediglich einen eindeutigen individuellen 21-Bit-Codepunkt dar (gespeichert als 32-Bit-Int). Methoden, die sich auf Codepunkte beziehen, könnten möglicherweise stattdessen ein Rune zurückgeben. Vermutlich gibt es in string einige Funktionen, mit denen Sie Rune aufzählen können.

Ich denke, es gibt ein paar offensichtliche Punkte, über die wir uns bei so etwas einigen müssen:

  1. Ist es sinnvoll, einen Rune -Typ zu erstellen, anstatt Int32 zu verwenden, wie dies bei aktuellen Methoden der Fall ist?
  2. Ist das Wort „Rune“ eigentlich eine gute Wahl?

Um (1) zu beantworten, brauchen wir meiner Meinung nach eine ausführlichere Beschreibung, wie Rune exponiert werden würde, welche Methoden es empfangen und zurückgeben würden usw. Und um festzustellen, ob das besser ist, als diese mit Int32 umgehen zu lassen

Was (2) angeht, bin ich selbst etwas zögerlich. "Rune" ist eine Art esoterisches Wort im Englischen und hat einige ungewöhnliche Konnotationen für seine Verwendung in diesem Zusammenhang. Es gibt auch den Punkt, den andere ansprechen: Es kollidiert mit einem anderen Unicode-Konzept. Wenn ich nach „Unicode Rune“ suche, erhalte ich hauptsächlich Ergebnisse für den Runic-Unicode-Block und nur wenige der Go-Sprachdokumentation.

char ist sowohl ein halbes als auch ein ganzes Wort; und Sie müssen seine Umgebung untersuchen, um festzustellen, welche - wie es aktuell einen halben oder einen ganzen Buchstaben darstellt.

Vielleicht System.character wo es immer ein ganzer Buchstabe ist... :sonnenbrille:

char ist eine etwas schreckliche Darstellung und sogar für reine ASCII/Latein-Sprachen; der Aufstieg von Emoji wird noch durchdringen; es bedeutet, dass char ein Scheck ist und vielleicht der nächste char -Typ ist

@NickCraver auf Twitter

Während utf8 eine Codierung mit variabler Breite ist; es ist selten (wenn überhaupt?), dass ein Benutzer mit halben Zeichen umgehen möchte; sowohl für utf8 als auch für utf32.

Ein 32-Bit-Typ würde sich gut für die Aufzählung eignen.

Schwieriger wäre indexOf, Length usw. aus Performance- oder Speicherperspektive.

  1. Byte-Array ist die beste Darstellung für ein undurchsichtiges Format; B. Beibehaltung des Formats in seinem ursprünglichen Format oder einem endgültigen Format (Dateiübertragung, Übertragung usw.)
  2. Byte-Array ist die beste Darstellung für Speicherbandbreite und Speichergröße
  3. byte array stimmt mit Position und indexOf, Length usw. in Bytes überein

Wenn Sie sich jedoch um tatsächliche Zeichen kümmern, Großbuchstaben schreiben und nach Zeichen aufteilen; Um zu verstehen, was ein Zeichen ist, wird Byte zu einer variablen Breite. Char macht das nicht wirklich besser; es verdoppelt die Größe der kleinsten Zeichen; enthält mehr Zeichen, hat aber immer noch eine variable Breite.

Dafür könnte ein 32-Bit-Wert aus Sicht des Benutzercodes sehr nützlich sein. Es hat jedoch Probleme mit Position, Länge und sekundären Elementen (indexOf usw.).

Ich bin sehr scharf auf einen reinen ASCII-String und einen utf8-String "Compact String Implementation" https://github.com/dotnet/coreclr/issues/7083; für die schnelle Verarbeitung von reinen ASCII-Strings

Gegen alles, was ich dort argumentiert habe, frage ich mich jedoch, wie eine 32-Bit-Darstellung von utf8 aussehen würde? Position würde auf Position abgebildet; Das Suchen von Zeichen wäre schnell, da es in ASCII ist, Elemente sind in nativen Größen usw. Wie würde es sich gegen die Verarbeitung jedes Bytes oder Zeichens stapeln, um seine Größe zu bestimmen?

Umrechnung hin und her wäre teurer; es wäre also eher ein Verarbeitungsformat; als ein Speicherformat.

@migueldeicaza wie ich verstehe, beziehen Sie sich nur auf die Erweiterung des Einzelzeichenformats von 16-Bit-Zeichen auf 32-Bit, sodass alle Darstellungen im Wert enthalten sind. eher als die Möglichkeit eines Halbwerts - eher als unbedingt das interne Format.

Allerdings ist etwas zu beachten (z. B. Positionsverhältnis und Suchkosten usw.)

Übrigens: Swift handelt auch mit ganzen Zeichenformaten

Swift bietet verschiedene Möglichkeiten, auf Unicode-Darstellungen von Zeichenfolgen zuzugreifen. Sie können die Zeichenfolge mit einer for-in-Anweisung durchlaufen, um auf ihre einzelnen Zeichenwerte als erweiterte Unicode-Graphem-Cluster zuzugreifen. Dieser Vorgang wird in Arbeiten mit Zeichen beschrieben.

Greifen Sie alternativ auf einen String-Wert in einer von drei anderen Unicode-kompatiblen Darstellungen zu:

  • Eine Sammlung von UTF-8-Codeeinheiten (auf die mit der utf8-Eigenschaft der Zeichenfolge zugegriffen wird)
  • Eine Sammlung von UTF-16-Codeeinheiten (auf die mit der utf16-Eigenschaft der Zeichenfolge zugegriffen wird)
  • Eine Sammlung von 21-Bit-Unicode-Skalarwerten, die der UTF-32-Codierungsform der Zeichenfolge entsprechen (auf die mit der Eigenschaft unicodeScalars der Zeichenfolge zugegriffen wird).

Ich habe es in der ursprünglichen Ausgabe gesagt und werde es noch einmal sagen. Aufzugeben, was ein Standard sagt, weil Sie den Ausdruck nicht mögen, wird mehr verwirren als lösen, und angesichts der Tatsache, dass es eine Runencodepage in Unicode gibt, verwirrt das nur noch mehr.

Der Name ist falsch.

@mellinoe

Die Rune würde viele der Operationen bereitstellen, die Sie heute auf einem Char erwarten, wie ToLower[Invariant], ToUpper[Invariant], ToTitle, IsDigit, IsAlpha, IsGraphic, IsSymbol, IsControl.

Darüber hinaus würde es Folgendes bereitstellen:

  • EncodeRune (codiert eine Rune in einen Bytepuffer)
  • RuneUtf8Len (gibt die Anzahl der Bytes zurück, die benötigt werden, um die Rune in UTF8 zu codieren),
  • IsValid (nicht alle Int32-Werte sind gültig)

Und interoperieren Sie mit String und Utf8string nach Bedarf.

Ich habe die Go-String-Unterstützung auf .NET portiert/angepasst, und sie bietet einen Blick darauf, wie diese Welt aussehen würde (dies ist ohne Laufzeithilfe):

https://github.com/migueldeicaza/NStack/tree/master/NStack/unicode

@benaadams sagte:

Ich frage mich, wie eine 32-Bit-Darstellung von utf8 aussehen würde? Position würde auf Position abgebildet; Das Suchen von Zeichen wäre schnell, da es in ASCII ist, Elemente sind in nativen Größen usw. Wie würde es sich gegen die Verarbeitung jedes Bytes oder Zeichens stapeln, um seine Größe zu bestimmen?

UTF8 ist eine In-Memory-Darstellung, die weiterhin existieren und weiterhin die Darstellung sein würde (und hoffentlich ist dies die längerfristige interne Codierung für zukünftige Zeichenfolgen in .NET).

Sie würden die vorhandenen UTF16-Strings (System.String) oder die kommenden UTF8-Strings (Utf8String) nicht in Chars decodieren (aus dem Grund, dem Sie und ich uns einig sind), sondern in Runes.

Einige Beispiele, konvertieren Sie eine Utf8-Zeichenfolge in Runen:

https://github.com/migueldeicaza/NStack/blob/6a071ca5c026ca71c10ead4f7232e2fa0673baf9/NStack/strings/usstring.cs#L756

Enthält ein utf8-String eine Rune:

https://github.com/migueldeicaza/NStack/blob/6a071ca5c026ca71c10ead4f7232e2fa0673baf9/NStack/strings/usstring.cs#L855

Mir ist gerade aufgefallen, dass ich den Indexer nicht implementiert habe ("Get me the n-th rune")

Die Zugriffsgeschwindigkeit auf die N-te Rune in einer Zeichenfolge ist eine Funktion des Speichers, nicht der Rune selbst. Wenn Ihr Speicher beispielsweise UTF32 ist, haben Sie direkten Zugriff auf jede Rune. Das ist akademisch, da niemand das benutzt. Der Zugriff auf das N-te Element auf UTF16 und UTF8 erfordert das ordnungsgemäße Scannen der Elemente, aus denen die Zeichenfolge besteht (Bytes oder 16-Bit-Ganzzahlen), um die richtige Grenze zu bestimmen. Nicht zu verwechseln mit String[int n] { get; } , das unabhängig von der Korrektheit nur das n-te Zeichen zurückgibt.

@benaadams Der Swift-Charakter ist eine Stufe höher als eine Rune. Zeichen in Swift sind "erweiterte Graphem-Cluster", die aus einer oder mehreren Runen bestehen, die, wenn sie kombiniert werden, ein für Menschen lesbares Zeichen ergeben.

Das Swift-Zeichen hat also keine feste 32-Bit-Größe, sondern eine variable Länge (und wir sollten auch dieses Konstrukt haben, aber das gehört zu einem anderen Datentyp). Hier ist das Beispiel von dieser Seite, aber dies erstreckt sich auch auf die Einstellung des Farbtons eines Emoji:

Hier ist ein Beispiel. Der Buchstabe é kann als einzelner Unicode-Skalar é (Lateinischer Kleinbuchstabe E mit Akut oder U+00E9) dargestellt werden. Derselbe Buchstabe kann jedoch auch als Skalarpaar dargestellt werden – ein Standardbuchstabe e (LATEINISCHER KLEINBUCHSTABE E oder U+0065), gefolgt von dem Skalar mit AKUTEM AKZENT (U+0301). Der Skalar COMBINING ACUTE ACCENT wird grafisch auf den Skalar angewendet, der ihm vorangeht, wodurch ein e in ein é umgewandelt wird, wenn es von einem Unicode-fähigen Textwiedergabesystem gerendert wird.

Nur für mich wäre das Wort grapheme selbstbeschreibender.

Meine zwei Cent zum Namen, wobei ich noch einmal den Go-Post über Saiten mit Betonung zitiere:

Codepunkt “ ist ein bisschen wie ein Schluck, also führt Go einen kürzeren Begriff für das Konzept ein: Rune. Der Begriff taucht in den Bibliotheken und im Quellcode auf und bedeutet genau dasselbe wie "Codepunkt" mit einem interessanten Zusatz.

Ich stimme @blowdart zu 100% zu, es Rune zu nennen ist einfach verwirrend und falsch. Der Unicode-Standard erwähnt Codepunkte dreimal nur auf der ersten Seite des Einführungskapitels , aber der Begriff Rune taucht nirgendwo auf.

Wenn es sich um einen Codepunkt handelt, sollte er Codepunkt heißen, ganz einfach.

Wenn der Begriff Rune nie im Standard auftaucht, könnte es in Ordnung sein, das Problem ist, dass er in Kapitel 8 mehrmals in Bezug auf Runen vorkommt. Es ist nicht nur falsch, es verwechselt die Angelegenheit aktiv mit einer anderen.

Nur für mich wäre das Wort grapheme selbstbeschreibender.

Wenn es um 32-Bit-Codepunkte geht, wäre der Begriff grapheme verwirrend, weil ein Graphem wieder etwas anderes ist.

Ich wollte oft einen Codepunkt-Datentyp (nicht lange, da sich das, woran ich gearbeitet habe, geändert hat, aber vor ein paar Jahren wollte ich das sehr und schrieb überlappende Teillösungen für Teile dieses Bedarfs und hätte eine gut getestete Bibliothek vertragen können). Ich sehe nicht ein, warum das nicht so heißen sollte wie CodePoint . Die meisten Leute, die erkennen, dass sie einen solchen Typ brauchen, würden wahrscheinlich sowieso in Begriffen von Codepunkten denken, nicht in Begriffen von Runen; oder auch in Bezug auf Codepunkte und Runen als separate Teile ihrer Aufgabe. ᚱᚢᚾᚪ ᛒᛇᚦ ᛥᛁᛚᛖ ᛒᚱᚣᚳᛖᚢ/rúna béoþ stille bryceu/Runen werden immer noch verwendet. Runen brauche ich nur etwa einmal im Jahr, und zwar eher mit Pergament und Tinte als mit irgendetwas Digitalem, aber es gibt bestimmt auch Leute, die damit digital umgehen. (Selbst bei Daten aus dem 20. Jahrhundert kenne ich einen Fall, in dem sie zur Archivierung von Daten aus der Zeit des Zweiten Weltkriegs verwendet wurden).

Grapheme ist noch kniffliger, da man oft Oktette → Zeichen (von .NET bereits gut gehandhabt) dann Zeichen → Codepunkte und dann Codepunkte → Grapheme verwenden möchte.

Kennzeichnen Sie dies vorerst als verfügbar.

Nächste Schritte : Was wir suchen, ist: ein formaler Vorschlag, der das Feedback von oben enthält (die tatsächliche Benennung des Typs und die Vorteile dieser Verwendung im Gegensatz zur Verwendung eines Int32).

Ich habe das Problem aktualisiert, sowohl mit der vorgeschlagenen API als auch mit einer ersten Implementierung:

https://github.com/migueldeicaza/NStack/blob/master/NStack/unicode/Rune.cs

Bei der Benennung des Typs geht es sowohl darum, einen Ort zu haben, an dem Sie nach den gültigen Operationen für den Typ suchen können, als auch um typspezifische Fähigkeiten (einige Beispiele finden Sie in der Implementierung).

@migueldeicaza, bevor Sie es als zur Überprüfung bereit kennzeichnen, was denken Sie über die Bedenken hinsichtlich der tatsächlichen Benennung des Typs, glauben Sie, dass CodePoint vielleicht besser in Bezug auf die Beschreibung des Typs sein könnte?

Ich denke, das Argument für die Verwendung von Codepoint als Name ist schwach.

Es zu verwenden ist eine schreckliche Idee, langfristig muss dies jede einzelne Verwendung von "char" in bestehendem Code ersetzen - wenn wir hoffen, eine angemessene Unicode-Unterstützung zu erhalten.

Ich wünschte, wir hätten "char" wie Rust verwenden können, aber leider haben wir es bereits genommen und wir haben ein kaputtes.

Go, diesen Namen angenommen zu haben, ist ein guter Präzedenzfall.

Ich stimme zu, dass code point hier nicht der richtige Begriff ist. Zumindest enthält er laut Unicode-Standard keine Werte über 10FFFF (http://unicode.org/glossary/#code_point).

Ich mag den Begriff rune nicht. Ich denke, es hat eine bestehende Verwendung in Unicode und anderswo, die insgesamt nur Verwirrung stiften wird. Ich denke auch, dass es eine ziemlich gute Chance gibt, mit bestehenden Benutzertypen in Konflikt zu geraten (insbesondere für Dinge wie Unity, wo eine „Rune“ ein bestimmtes Spielobjekt darstellen könnte).

Ich mag jedoch die Idee eines Typs, der den Typ char32_t C++ 11 abdeckt, nur mit einem anderen Namen.

Es gibt etwas zu sagen für Char32 . Es ist auf den Punkt gebracht, es ist analog zu den Typnamen der ganzzahligen Typen. Es spricht eher auf der konzeptionellen Ebene der Charaktere als auf der Ebene der Codepunkte. Es ist nicht der Name eines Skripts.

Da wir nint haben wollen, wie wäre es mit nchar ?

Der Präzedenzfall wäre in den Datenbanken nchar und nvarchar

Wobei nchar Nationalzeichen / Nationalzeichen sind und nvarchar Nationalzeichen variieren / Nationalzeichen variieren; In welchen Feldtypen können Sie Unicode speichern, auch in einigen ISO-Standards - nicht sicher, welche, vielleicht SQL?

Was ist diese Unicode-Verwendung von Rune? Das ist mir neu.

U+16A0 bis U+16F8

Es wird verwendet, um auf eine bestimmte Codepage im Unicode-Standard zu verweisen. Es wurde einige Male in diesem Thread angesprochen: http://unicode.org/charts/PDF/U16A0.pdf

Ah, Runen, nicht Runen.

Der Sicherungsname (System.Rune oder System.Char32) ist nicht so wichtig wie die Bezeichnung, die in C# projiziert wird.

Erstens: ja, ja, und bitte mehr davon. Ich liebe diese Idee (ehrlich gesagt, ich hatte schon lange eine ähnliche Idee). Tatsächlich verwenden wir seit einiger Zeit eine benutzerdefinierte Zeichenfolgenklasse und eine benutzerdefinierte Zeichenstruktur in unserer Git-Kompatibilität später in Visual Studio (Git spricht in UTF-8 und die Transcodierung von allem ist sehr langsam).

Können wir zum Thema statische Methodennamen bitte willkürliche Kurznamen vermeiden? Da Char.IsPunctuation die aktuelle Methode ist, können wir das bitte mit Rune.IsPunctuation oder ähnlichem spiegeln?

Unter der Annahme (immer gefährlich), dass dies akzeptiert wird, können wir ein intrinsisches rune oder c32 haben oder einfach char vollständig durch die Implementierung System.Rune ersetzen?

Ich schlage unichar oder uchar vor, obwohl uchar so aussehen würde, als wäre es ein Zeichen ohne Vorzeichen. Was auch immer gewählt wird, ich hoffe, wir bekommen einen sprachspezifischen Alias ​​dafür. Ich persönlich bin ein großer Fan der Verwendung von Sprachaliasen für primitive Typen.

Ich stimme auch @whoisj zu - Würde auf jeden Fall vollständige Methodennamen gegenüber Kurznamen/Abkürzungen bevorzugen.

Ich stimme auch @whoisj zu - Würde auf jeden Fall vollständige Methodennamen gegenüber Kurznamen/Abkürzungen bevorzugen.

IMO muss eine Sprache (und ihre Bibliotheken) entweder vollständige, abgekürzte Namen auswählen oder sich auf die Abkürzungen einlassen (wie C mit strcmp, memcpy usw.).

oder einfach char komplett durch die Implementierung System.Rune ersetzen?

Das wäre aus ziemlich offensichtlichen Gründen eine bahnbrechende Änderung.

Das wäre aus ziemlich offensichtlichen Gründen eine bahnbrechende Änderung.

Meine Kommentare waren größtenteils ironisch und hoffnungsvoll. Ein 16-Bit-Typ für Zeichen war von Anfang an ein Fehler.

Guter Fang bei der Namensgebung, wird behoben.

Es gibt andere kleine Inkonsistenzen in der bereitgestellten API, die wir ebenfalls beheben werden.

@migueldeicaza

Ah, Runen, nicht Runen.

Runic ist das Adjektiv, Rune das Substantiv. Alle Runenzeichen sind Runen.

_Runic_ ist das Adjektiv, _rune_ das Substantiv. Alle Runenzeichen sind Runen.

So fair es scheint, "Cortana: define _'rune'_" kommt mit:

ein Buchstabe eines altgermanischen Alphabets, verwandt mit dem römischen Alphabet.

Ah ja, immer wenn ich das Wort "Rune" sehe, denke ich sofort an dieses obskure Kapitel einer Spezifikation, die niemand gelesen hat und das über "The Runic Unicode Block" spricht.

😆 Ich denke an Kindheitserinnerungen an das Lesen von Tolkien.

ᛁ᛫ᚦᛁᛜᚲ᛫ᛟᚠ᛫ᚱᚢᚾᛖᛋ

Ja, ich denke nicht speziell an die Spezifikation, aber ich denke an die Art von Charakteren, auf die sich die Spezifikation bezieht.

Du sagst rune und ich denke an Magie, Fantasie, kryptische Rätsel, alte Sprachen usw.

Ich bin froh, dass Sie das Wort „Rune“ nicht sehen und sofort denken: „Ah, das bezieht sich eindeutig auf den Unicode 7.0-Runenblock, dessen Wert auf diese eindeutigen Werte im Bereich 16A0..16F8 begrenzt sein wird“.

Ich weiß, dass Tanner hier eine einzige Stimme ist, und einige von Ihnen denken immer noch "Aber Miguel, ich sehe das Wort 'Rune' und mir fällt sofort ein Datentyp ein, der jemals nur 88 mögliche Werte enthalten könnte". Wenn dies ein Problem ist, das Sie damit zu kämpfen haben, mein Bruder / meine Schwester, habe ich Neuigkeiten für Sie: Sie haben größere Fische zum Braten.

Ich verfolge diesen Thread seit etwas mehr als einem Monat mit einer Mischung aus Aufregung und Zögern. Ich habe letzten Monat an der Internationalization and Unicode Conference teilgenommen, und keine der Präsentationen befasste sich mit .NET. Es gibt ein Wahrnehmungsproblem mit dem .NET Framework; eine, die angesichts der Geschichte ihrer Globalisierungsmerkmale nicht unbedingt unverdient ist. Abgesehen davon liebe ich das Programmieren in C# und möchte unbedingt neue Funktionen sehen, die den Platz von .NET in einer wirklich globalen Community stärken. Ich denke, dieser Vorschlag ist ein guter Schritt in diese Richtung, um die Standards zu übernehmen, die die Internationalisierungsgemeinschaft von Software erwartet.

Mein Zögern war hauptsächlich wegen des Gezänks über den Typennamen. Es stimmt zwar, dass die Designer von Go den Namen „Rune“ gewählt haben, aber das ist aus dem oben mehrfach aufgeführten Grund problematisch: Es gibt Codepunkte, die eigentlich Runen heißen. Es fällt mir schwer, einem Vorschlag zuzustimmen, der versucht, sich eng an einen anerkannten Standard zu halten, und dann die Terminologie neu definiert, die Teil der Spezifikation ist. Darüber hinaus ist das Argument, dass die meisten Entwickler den Begriff nicht kennen, fadenscheinig, da die Entwickler, die am meisten an der korrekten Verwendung dieses Typs interessiert sind, eher die Unicode-Spezifikation verstehen und eine gute Vorstellung davon haben, was eine "Rune" tatsächlich ist. Stellen Sie sich die Kuriosität vor, die entstehen könnte, wenn Sie die Terminologie verwechseln:

Rune.IsRune(new Rune('ᛁ')); // evaluates to true
Rune.IsRune(new Rune('I')); // evaluates to false

Natürlich bin ich hier den einfachen Weg gegangen, zu kritisieren, ohne einen neuen Namen zu nennen. Ich denke, der vorherige Vorschlag von CodePoint ist die selbsterklärendste Option (und erscheint in der ursprünglichen Problembeschreibung), aber char32 hätte mehr Parität mit den vorhandenen primitiven Typen (obwohl ich es tun würde zögern zu sagen, dass nicht jeder Codepunkt ein Zeichen ist). Wenn das Ziel darin besteht, eine bessere Unicode-Unterstützung in .NET einzubauen, unterstütze ich diesen Weg absolut, aber der beste Weg, dies zu tun, ist, der Spezifikation zu folgen.

Drei Vorschläge:

  1. Der Rune-Klasse fehlt das kritische „IsCombining“. Ohne das können wir eine Reihe von Runen (Codepunkten) nicht in eine Reihe von Graphemen umwandeln.
  1. Ich hätte gerne auch eine entsprechende Grapheme-Klasse. Ein Graphem ist in diesem Zusammenhang wirklich nur eine Liste von einer oder mehreren Runen (Code Points), so dass die erste Rune nicht kombiniert wird und die restlichen Runen kombiniert werden. Der Anwendungsfall ist, wenn ein Entwickler mit Blöcken von „sichtbaren Zeichen“ umgehen muss. Zum Beispiel ist ein + GRAVE zwei Runen, die ein Graphem bilden.

  2. Beim Netzwerken erhalten wir oft einen Haufen Bytes, die wir in ein "String"-ähnliches Objekt umwandeln müssen, bei dem die Bytes möglicherweise nicht vollständig sind (z. B. werden uns einige Bytes mitgeteilt, aber das letzte Byte in einer Multibyte-Sequenz ist nicht noch nicht ganz angekommen). Ich sehe keine offensichtliche Möglichkeit, einen Bytestrom in einen Runenstrom umzuwandeln, sodass das Fehlen des letzten Bytes einer Multibyte-Sequenz als normale Situation angesehen wird, die behoben wird, wenn wir den nächsten Bytesatz erhalten.

Und schließlich verwenden Sie bitte Unicode-Namen und nennen Sie dies einen CodePoint. Ja, das Unicode-Konsortium leistet schreckliche Arbeit, um den Unterschied zu erklären. Aber die Lösung besteht darin, eine klare und brauchbare Dokumentation hinzuzufügen; alles andere verwirrt das Problem, anstatt zur Klärung beizutragen.

Ich weiß nicht, wo ich mit der Kombinationsanforderung beginnen soll, weder Go, Rust noch Swift bieten eine solche API für Rune, Character oder Unicode Scalar (ihre Namen für System.Rune ). Bitte geben Sie einen Implementierungsvorschlag an.

Auf Graphem-Clustern ist es eine gute Idee, es sollte unabhängig von System.Rune verfolgt werden. Für das, was es wert ist, verwendet Swift dafür Character , aber Swift ist auch kein großartiges Modell für den Umgang mit Strings.

Das Umwandeln von Byteströmen in eine richtige Rune ist ein Problem, das zu einer API auf höherer Ebene gehört. Sie können sich jedoch meine ustring -Implementierung ansehen, die dasselbe Substrat wie meine System.Rune -Implementierung verwendet, um zu sehen, wie diese Puffer in utf8-Strings abgebildet werden:

https://github.com/migueldeicaza/NStack/blob/master/NStack/strings/usstring.cs

Dokumentation, die ich noch nicht aktualisiert habe, seit ich System.Rune in die API eingeführt habe, aber sie abdeckt:

https://migueldeicaza.github.io/NStack/api/NStack/NStack.usstring.html

Was die Namensgebung angeht, ist Rust mit char eindeutig die beste, aber wir haben das vermasselt. Das zweitbeste ist Go with rune . Alles, was größer als vier Zeichen ist, wird für die Leute nur lästig sein, um das Richtige zu tun.

Es tut mir Leid; Ich finde CodePoint ist ein außergewöhnlich guter Name. Es ist selbsterklärend, einprägsam und wird mit c p automatisch vervollständigt.

IsCombining wäre definitiv notwendig, aber auch die Kombinationsklasse zu kennen, und wenn wir einmal haben, dass IsCombining größtenteils Zucker ist, da es nur IsCombining => CombiningClass != 0 oder IsCombining => CombiningClass != CombiningClass.None ist. Graphem-Cluster wären zwar wieder außerhalb davon, aber der Ausgangspunkt wäre die Kenntnis der Kombinationsklasse für Standard-Clustering, Neuordnung usw.

CodePoint ist ein großartiger Name für einen Typ über Codepunkte, und vier Zeichen sind kaum eine Grenze, mit der wir uns bei anderen stark verwendeten Typen auseinandersetzen müssen; string ist 50 % größer und hindert uns nicht daran, es regelmäßig zu verwenden. Vier zufällig ausgewählte Buchstaben wären ein besserer Name, als Gos Fehler zu wiederholen.

Da uint nicht CLS-konform ist, gibt es keinen CLS-konformen Ctor, der die Astralebenen abdeckt. int wäre auch nötig.

Implizite Zwei-Wege-Konvertierungen können bei Überladungen dazu führen, dass schlimme Dinge passieren, daher sollte eine Richtung vielleicht explizit sein. Es ist nicht klar, welche. Einerseits ist uint / int breiter als Codepunkte, da Werte unter 0 oder über 10FFFF 16 nicht aussagekräftig sind, und diese implizite Konvertierung ermöglicht eine schnellere Verwendung von mehr vorhandenen APIs für Zahlen. Auf der anderen Seite kann ich sehen, dass ich häufiger von einer Zahl zu einem Codepunkt werfen möchte als umgekehrt.

Da uint nicht CLS-konform ist, gibt es keinen CLS-konformen ctor, der die Astralebenen abdeckt. int wäre auch notwendig.

Es sei denn, es wurde ein neuer intrinsischer Typ in die gemeinsame Sprache eingeführt.

JonHanna - meinst du, dass diese drei Konstruktoren:
öffentlicher statischer impliziter Operator uint (Runenrune);
öffentlicher statischer impliziter Operator Rune (char ch);
öffentlicher statischer impliziter Operator Rune (uint-Wert);

sollte "int" statt "uint" sein. AFAICT, int deckt leicht den gesamten Satz von astralen (Nicht-BMP) Ebenen ab.

@PeterSmithRedmond Ich meine, dass es neben den beiden Konstruktoren, von denen einer char und einer uint nimmt, einen geben sollte, der int nimmt, aber ja, es sollte auch ein int geben implicit sein sollte und was explicit ist eine andere Frage). Es kann auch nicht schaden, uint für die Sprachen zu haben, die es verwenden können; es ist schließlich ein ganz natürliches Spiel.

Wenn dies System.Char ersetzen soll, sollte es möglich sein, "Arithmetik" damit durchzuführen (dh ==, !=, >, < unsicher bei +, -, *, /) und, was noch wichtiger ist, es sollte Unterstützung für Literale davon geben Typ zum Beispiel Ich sollte in der Lage sein zu schreiben:

rune r = '𐍈'; // Ostrogothic character chose on purpose as in UTF16 will be a "surrogate pairs"


image

Wenn nicht rune , ist das einzige andere Synonym von character , das funktionieren könnte, vielleicht letter ?

Substantiv

  1. eine schriftliche oder gedruckte Mitteilung, die an eine Person oder Organisation gerichtet ist und normalerweise per Post übermittelt wird.
  2. ein Symbol oder Zeichen, das herkömmlicherweise beim Schreiben und Drucken verwendet wird, um einen Sprachlaut darzustellen, und das Teil eines Alphabets ist.
  3. ein Drucktyp, der ein solches Symbol oder Zeichen trägt.

Obwohl das mit Buchstabe vs. Zahl in Konflikt stehen würde

Buchstabe hat in Unicode (und Net im Allgemeinen) eine noch genauere Bedeutung als Rune.

Ich denke, wenn wir dies zu einem Unicode-Zeichentyp machen wollen, müssen wir den Namenskonventionen von Unicode folgen. was _"Codepunkt"_ bedeutet.

Codepunkt . (1) Jeder Wert im Unicode-Codespace; das heißt, der Bereich von ganzen Zahlen von 0 bis 10FFFF16. (Siehe Definition D10 in Abschnitt 3.4, Zeichen und Codierung .) Nicht alle Codepunkte sind codierten Zeichen zugeordnet. Siehe Codepunkttyp . (2) Ein Wert oder eine Position für ein Zeichen in einem beliebigen codierten Zeichensatz.

Oder vielleicht geben wir einfach auf und nennen eine Ente eine "Ente" und bezeichnen sie als Unicode-Zeichen (auch bekannt als uchar ).

Warum lösen Sie das nicht einfach, um stattdessen System.CodePoint zu verwenden?
Imho ist es in Bezug auf die Terminologie von Unicode richtiger, und andere Leute in der Java-Welt verwenden es. Anstatt also einen eigenen Begriff zu haben, halten wir uns an Unicode-Begriffe. Es ist sinnvoller und universeller in Bezug auf allgemeine Zeichen und die Implementierung von Zeichenfolgen in .NET, auch wenn man die Tatsache kennt, dass String in .NET eine Sammlung von Zeichen ist und diese Sammlung von Zeichen auf Unicode basiert.

Ich weiß das, weil ich sowohl in der Java- als auch in der .NET-Welt gelebt habe.
Und vielleicht fangen wir damit an, einen Implementierungsentwurf dazu zu haben.

Es gibt wirklich zwei Komponenten davon und beide wären erforderlich (CodeUnit in https://github.com/dotnet/corefxlab/issues/1799 von @GrabYourPitchforks)

C# keyword      Ugly Long form      Size
----------------------------------------
ubyte      <=>  System.CodeUnit    8 bit  - Assumed Utf8 in absence of encoding param
uchar      <=>  System.CodePoint  32 bit

CodeUnit / ubyte sind wichtig für die Darstellung der Codierung mit variabler Breite und für die Verwendung in Span<ubyte> , um sicherzustellen, dass Text-APIs für Texttypen, aber nicht für Rohbytes verfügbar sind.

CodePoint / uchar ist wichtig für eine sinnvolle Verarbeitung; zB .IndexOf(❤) als ubyte allein kann nicht verwendet werden, um nach einem Multibyte-Unicode-Zeichen zu suchen; und das Aufzählen von mehr als ubyte s wäre voller Gefahren, also sollte der Enumerator in Einheiten uchar arbeiten.

Die Kombination der beiden Vorschläge wäre so etwas wie

using System;
using System.Runtime.InteropServices;

// C# Keywords
using ubyte = System.CodeUnit;
using uchar = System.CodePoint;
using uspan = System.Utf8Span;
using ustring = System.Utf8String;

namespace System
{
    public ref struct Utf8Span
    {
        private readonly ReadOnlySpan<ubyte> _buffer;

        public Utf8Span(ReadOnlySpan<ubyte> span) => _buffer = span;
        public Utf8Span(uspan span) => _buffer = span._buffer;
        public Utf8Span(ustring str) => _buffer = ((uspan)str)._buffer;
        public Utf8Span(ReadOnlyMemory<ubyte> memory) => _buffer = memory.Span;

        // Returns the CodeUnit index, not CodePoint index
        public int IndexOf(char value) => IndexOf(value, 0);
        public int IndexOf(char value, int startIndex) => IndexOf(value, 0, _buffer.Length);
        public int IndexOf(char value, int startIndex, int count);
        public int IndexOf(char value, StringComparison comparisonType);

        public int IndexOf(uchar value) => IndexOf(value, 0);
        public int IndexOf(uchar value, int startIndex) => IndexOf(value, 0, _buffer.Length);
        public int IndexOf(uchar value, int startIndex, int count);
        public int IndexOf(uchar value, StringComparison comparisonType);

        public uspan Substring(int codeUnitIndex);
        public uspan Substring(int codeUnitIndex, int codePointCount);

        public bool StartsWith(uchar ch) => _buffer.Length >= 1 && _buffer[0] == ch;
        public bool StartsWith(ustring str) => StartsWith((uspan)str);
        public bool StartsWith(uspan value) => _buffer.StartsWith(value._buffer);
        public bool EndsWith(uchar ch) => _buffer.Length >= 1 && _buffer[0] == ch;
        public bool EndsWith(ustring str) => EndsWith((uspan)str);
        public bool EndsWith(uspan value) => _buffer.EndsWith(value._buffer);

        public Enumerator GetEnumerator() => new Enumerator(this);

        // Iterates in uchar steps, not ubyte steps
        public ref struct Enumerator
        {
            public Enumerator(uspan span);

            public uchar Current;
            public bool MoveNext();
            public void Dispose() { }
            public void Reset() => throw new NotSupportedException();
        }
    }

    public class Utf8String
    {
        private readonly ReadOnlyMemory<ubyte> _buffer;

        public Utf8String(ustring str) => _buffer = str._buffer;
        public Utf8String(ReadOnlyMemory<ubyte> memory) => _buffer = memory;

        public bool StartsWith(uchar ch) => ((uspan)this).StartsWith(ch);
        public bool StartsWith(ustring value) => ((uspan)this).StartsWith(value);
        public bool StartsWith(uspan value) => ((uspan)this).StartsWith(value);
        public bool EndsWith(uchar ch) => ((uspan)this).EndsWith(ch);
        public bool EndsWith(ustring value) => ((uspan)this).EndsWith(value);
        public bool EndsWith(uspan value) => ((uspan)this).EndsWith(value);

        public static implicit operator uspan(ustring value) => new uspan(value._buffer);

        // Returns the CodeUnit index, not CodePoint index
        public int IndexOf(char value) => IndexOf(value, 0);
        public int IndexOf(char value, int startIndex) => IndexOf(value, 0, _buffer.Length);
        public int IndexOf(char value, int startIndex, int count);
        public int IndexOf(char value, StringComparison comparisonType);

        public int IndexOf(uchar value) => IndexOf(value, 0);
        public int IndexOf(uchar value, int startIndex) => IndexOf(value, 0, _buffer.Length);
        public int IndexOf(uchar value, int startIndex, int count);
        public int IndexOf(uchar value, StringComparison comparisonType);

        public ustring Substring(int codeUnitIndex);
        public ustring Substring(int codeUnitIndex, int codePointCount);

        public uspan.Enumerator GetEnumerator() => ((uspan)this).GetEnumerator();
    }

    [StructLayout(LayoutKind.Auto, Size = 1)]
    public struct CodeUnit : IComparable<ubyte>, IEquatable<ubyte>
    {
        private readonly byte _value;

        public CodeUnit(ubyte other) => _value = other._value;
        public CodeUnit(byte b) => _value = b;

        public static bool operator ==(ubyte a, ubyte b) => a._value == b._value;
        public static bool operator !=(ubyte a, ubyte b) => a._value != b._value;
        public static bool operator <(ubyte a, ubyte b) => a._value < b._value;
        public static bool operator <=(ubyte a, ubyte b) => a._value <= b._value;
        public static bool operator >(ubyte a, ubyte b) => a._value > b._value;
        public static bool operator >=(ubyte a, ubyte b) => a._value >= b._value;

        public static implicit operator byte(ubyte value) => value._value;
        public static explicit operator ubyte(byte value) => new ubyte(value);

        // other implicit conversions go here
        // if intrinsic then casts can be properly checked or unchecked

        public int CompareTo(ubyte other) => _value.CompareTo(other._value);

        public override bool Equals(object other) => (other is ubyte cu) && (this == cu);

        public bool Equals(ubyte other) => (this == other);

        public override int GetHashCode() => _value;

        public override string ToString() => _value.ToString();
    }

    [StructLayout(LayoutKind.Auto, Size = 4)]
    public struct CodePoint : IComparable<uchar>, IEquatable<uchar>
    {
        private readonly uint _value;

        public CodePoint(uint CodePoint);
        public CodePoint(char ch);

        public static ValueTuple<uchar, int> DecodeLastCodePoint(ubyte[] buffer, int end);
        public static ValueTuple<uchar, int> DecodeLastCodePoint(ustring str, int end);
        public static ValueTuple<uchar, int> DecodeCodePoint(ubyte[] buffer, int start, int n);
        public static ValueTuple<uchar, int> DecodeCodePoint(ustring str, int start, int n);
        public static int EncodeCodePoint(uchar CodePoint, ubyte[] dest, int offset);
        public static bool FullCodePoint(ubyte[] p);
        public static bool FullCodePoint(ustring str);
        public static int InvalidIndex(ubyte[] buffer);
        public static int InvalidIndex(ustring str);
        public static bool IsControl(uchar CodePoint);
        public static bool IsDigit(uchar CodePoint);
        public static bool IsGraphic(uchar CodePoint);
        public static bool IsLetter(uchar CodePoint);
        public static bool IsLower(uchar CodePoint);
        public static bool IsMark(uchar CodePoint);
        public static bool IsNumber(uchar CodePoint);
        public static bool IsPrint(uchar CodePoint);
        public static bool IsPunctuation(uchar CodePoint);
        public static bool IsSpace(uchar CodePoint);
        public static bool IsSymbol(uchar CodePoint);
        public static bool IsTitle(uchar CodePoint);
        public static bool IsUpper(uchar CodePoint);
        public static int CodePointCount(ubyte[] buffer, int offset, int count);
        public static int CodePointCount(ustring str);
        public static int CodePointLen(uchar CodePoint);
        public static uchar SimpleFold(uchar CodePoint);
        public static uchar To(Case toCase, uchar CodePoint);
        public static uchar ToLower(uchar CodePoint);
        public static uchar ToTitle(uchar CodePoint);
        public static uchar ToUpper(uchar CodePoint);
        public static bool Valid(ubyte[] buffer);
        public static bool Valid(ustring str);
        public static bool ValidCodePoint(uchar CodePoint);

        public static bool operator ==(uchar a, uchar b) => a._value == b._value;
        public static bool operator !=(uchar a, uchar b) => a._value != b._value;
        public static bool operator <(uchar a, uchar b) => a._value < b._value;
        public static bool operator <=(uchar a, uchar b) => a._value <= b._value;
        public static bool operator >(uchar a, uchar b) => a._value > b._value;
        public static bool operator >=(uchar a, uchar b) => a._value >= b._value;

        // etc
    }
}

Ich habe UnicodeScalar in meinen Prototypimplementierungen verwendet, um auf einen Unicode-Skalarwert (Werte im Bereich U+0000..U+10FFFF, einschließlich; ohne Ersatzcodepunkte) und Utf8Char zu verweisen um auf die UTF-8-Codeeinheit zu verweisen. Anscheinend bevorzugen viele Leute _Rune_ anstelle von _UnicodeScalar_, weil es weniger mundvoll ist. Es ist mir egal, aber ich möchte darauf hinweisen, dass der Begriff "Unicode-Skalarwert" derselbe Begriff ist, der von der Unicode-Spezifikation verwendet wird. ;)

Das .NET Framework hat auch das Konzept eines „Textelements“, bei dem es sich um einen oder mehrere Skalare handelt, die, wenn sie kombiniert werden, ein einziges unteilbares Graphem bilden. Mehr Infos dazu bei MSDN . Insbesondere wenn Sie eine Zeichenfolge auflisten, möchten Sie je nach Codeeinheit ( Utf8Char oder Char ), Skalarwert ( UnicodeScalar ) oder Textelement aufzählen besonderes Szenario. Idealerweise würden wir alle drei Typen sowohl über String als auch über Utf8String unterstützen.

Die API-Oberfläche für unseren Prototyp ist noch nicht fertig und unterliegt schnellen Änderungen, aber Sie können einige aktuelle Überlegungen unter https://github.com/dotnet/corefxlab/tree/utf8string/src/System.Text.Utf8/System sehen https://github.com/dotnet/corefxlab/blob/master/src/System.Text.Primitives/System/Text/Encoders/Utf8Utility.cs.

Etwas off-topic:
Sollte das „Textelement“ die durch „Grapheme Cluster Boundaries“ in UAX dotnet/corefx#29 definierte Segmentierung sein?

using System;
using System.Globalization;

class Program
{
    static void Main()
    {
        var e = StringInfo.GetTextElementEnumerator("👩🏻‍👦🏼👨🏽‍👦🏾‍👦🏿👩🏼‍👨🏽‍👦🏼‍👧🏽👩🏻‍👩🏿‍👧🏼‍👧🏾");
        while (e.MoveNext())
        {
            Console.WriteLine(e.GetTextElement());
        }
    }
}

erwartetes Ergebnis:
👩🏻‍👦🏼
👨🏽‍👦🏾‍👦🏿
👩🏼‍👨🏽‍👦🏼‍👧🏽
👩🏻‍👩🏿‍👧🏼‍👧🏾

tatsächliche Ergebnis:
👩
🏻

👦
🏼
👨
🏽

👦
🏾

👦
🏿
👩
🏼

👨
🏽

👦
🏼

👧
🏽
👩
🏻

👩
🏿

👧
🏼

👧
🏾

UnicodeScalar ist immer noch super einfach zu tippen. u s c Leerzeichen (automatische Vervollständigung) Da dies der korrekte, selbstbeschreibendste Begriff ist, hoffe ich wirklich, dass wir das verstehen.

@ufcpp Das ist ein guter Punkt. Eröffne dafür gerne ein neues Thema. Wenn wir das Verhalten aus Kompatibilitätsgründen nicht ändern können, würde ich vorschlagen, dass wir diesen Typ ablehnen und einen spezifikationskonformen Graphem-Enumerator erstellen.

ubyte / uchar sind verwirrend. Sie lauten wie unsigned char / unsigned byte angesichts der Konvention, die mit ushort / uint / ulong festgelegt wurde. Vielleicht sind char8 / u8char und char32 / u32char klarer?

Auf jeden Fall denke ich, dass wir falsch ausgerichtet sind, ob UTF-8-Codeeinheiten und Codepunkte sind:

  1. primitive Datentypen auf niedriger Ebene in .NET - wie byte , int
  2. ein Datenformat zum Konvertieren in/von bestehenden Primitiven - wie DateTime , Guid

Und wie stellen wir angesichts dieser Entscheidung Codepunkt-bezogene APIs bereit?

Option 1 bedeutet, dass Text über char8-, char16- und char32-Primitive (und begleitende u8string-, u16string- und u32string) wie C++17 behandelt wird. Dann ist char32 als rune ein schlechter Name, da wir bereits char16 als char haben und auch einen 3. Namen für char8 brauchen.

Option 2 bedeutet, dass byte und int/uint „gut genug“ sind, um UTF-Codeeinheiten und Codepunkte zu speichern. Dies impliziert, dass alle Zeichenfolgen UTF-16 bleiben. CodePoint / rune löst eher Probleme der Code Point Semantik als der binären Darstellung - und ist nicht für IO gedacht .

IMO UTF-8/UTF-32 sind nur Datenformate (Option 2). Behandeln Sie sie als Daten (Byte/int). CodePoint ist für mich eher wie DateTime oder Guid (ein anderer Bezeichner*) als int - kein primitiver Typ auf niedriger Ebene, der nicht direkt in IO unterstützt wird (dh BinaryWriter), keine Notwendigkeit für Intrinsics.

@miyu Der Prototyp, den wir in corefxlab aufbringen, kommt Option 1 näher. Es gibt bestimmte Datentypen zur Darstellung von Codeeinheiten, und diese Datentypen dienen der internen Darstellung von Textdaten und können nicht zur Übertragung von Textdaten über das Kabel verwendet werden. (Wie Sie darauf hinweisen, funktioniert .NET bereits heute so: System.Char ist die Codeeinheit eines UTF-16-Strings, aber System.Char kann nicht über die Leitung gesendet werden.)

Zusätzlich gibt es APIs zum Konvertieren zwischen byte[] / Span<byte> / usw. (dies ist die binäre Darstellung aller Daten und für I/O geeignet) und primitiven Typen wie Utf8String / String / Guid / usw. Einige davon sind einfacher als andere. Zum Beispiel können wir eine praktische Eigenschaft Utf8String.Bytes verfügbar machen, die ein ReadOnlySpan<byte> zur Verwendung in i/o zurückgibt, und dieser Eigenschafts-Getter kann O(1)-Komplexität haben. Wir würden eine solche Eigenschaft nicht für den Typ String einführen, obwohl Sie sich vorstellen könnten, eine bequeme Methode String.ToUtf8Bytes() zu haben. Und obwohl es eine Utf8String.Bytes Eigenschaft geben würde, wäre der elementare Typ der direkten Aufzählung über eine Utf8String Instanz nicht byte . Es wäre Utf8CodeUnit (Name TBD) oder UnicodeScalar , je nachdem, was unserer Meinung nach für die Arten von Anwendungen, die Entwickler erstellen möchten, sinnvoller ist.

Dumme Idee von der Wand - was ist mit wchar (_wide char_)? Heutzutage verwenden die meisten C- und C++-Compilerumgebungen (außerhalb von Windows) bereits wchar_t , um das funktionale Äquivalent einer 32-Bit-Codeeinheit darzustellen. Windows ist eine bemerkenswerte Ausnahme, wo wchar_t als 16-Bit-Typ definiert ist, aber Entwickler, die heute unter Windows p/aufrufen, müssen sich bereits der Unterschiede in der Bitbreite zwischen einem .NET char bewusst sein char .

Der Typ / das Schlüsselwort wchar würde gegen unsere Namenskonventionen verstoßen, aber wir werfen dies einfach zur Überlegung raus.

Dumme Idee von der Wand - was ist mit wchar (breites Zeichen)?

Funktioniert bei mir

Der Typ / das Schlüsselwort wchar würde gegen unsere Namenskonventionen verstoßen, ...

Klingt nicht so, als würden wir ein kurzes Schlüsselwort für die C#-Sprache erhalten

https://github.com/dotnet/apireviews/pull/64#discussion_r196962756 scheint es äußerst unwahrscheinlich, dass wir Sprachschlüsselwörter für diese Typen einführen würden, da diese kontextbezogen sein müssten (d. h. abhängig davon, ob sie sich in einen Typ mit auflösen lassen den Namen des Schlüsselworts, das sie noch an diesen Typ binden müssten, und nicht den durch das Schlüsselwort repräsentierten Typ).

Also, wenn wir etwas Nettes wollen... dh NotLotsOfCapitalFullWords ...

Obwohl ich normalerweise die Namenskonventionen von .NET mag, ist ein langer Name ein wenig anstößig für im Wesentlichen ein int , das wahrscheinlich auch in Generika und als Schleifenvariablen verwendet wird.

zB niemand tut

foreach (Int32 i in list)
{
    // ...
}

Tun sie? (Sicherlich...)

foreach (UnicodeScalar us in str)
{
    // ...
}

Ist weit schlimmer

foreach (wchar c in str)
{
    // ...
}

Scheint in Ordnung...

rune , wchar und uchar (vorgeschlagen in einem anderen Thread) klingen alle gut für mich. Irgendwelche Vorschläge für einen Peer von string ? wstring , ustring oder andere?

... und warum nicht ein Schlüsselwort für die C#-Sprache? Sicher, kein Schlüsselwort für die erste Veröffentlichung zu haben, ist sinnvoll, aber wenn dies die Zukunft für die Handhabung von Zeichenfolgen sein soll, ist es nicht nur unaufrichtig, sondern auch offen feindlich gegenüber seiner Einführung.

/CC @MadsTorgersen @jaredpar

Warum nicht ein Schlüsselwort für die C#-Sprache erhalten?

Neue Keywords sind zu 100 % Breaking Changes. Egal, welches Wort Sie wählen, es gibt ein Unternehmen, das eine Art dieses Namens hat, der überall in seinem Projekt verwendet wird. Die einzige Option, die wir haben, sind kontextbezogene Schlüsselwörter: var zum Beispiel.

Ich habe gemischte Gefühle, wenn ich dafür ein kontextbezogenes Schlüsselwort verwende. Die vorhandenen Typschlüsselwörter ( int , string , etc ...) haben einen konkreten Vorteil gegenüber dem eigentlichen Typnamen ( Int32 , String ):

  • string : Dies bezieht sich auf den Typ System.String in der Assembly, die der Compiler als Corelib identifiziert. Mit diesem Namen ist keine Mehrdeutigkeit verbunden.
  • String : Der Compiler hat kein Verständnis für diesen Typ. Es ist nur ein Typ wie jeder andere und durchläuft dieselben Suchregeln wie die von Ihnen definierten Typen. Es kann string entsprechen oder auch nicht.

Sobald wir hier kontextbezogene Schlüsselwörter eingeführt haben, könnte rune eines von beiden sein:

  • Der Typ System.Rune innerhalb der Corelib-Assembly
  • Der Typ rune , den Sie vor zwei Jahren definiert haben, als Sie über Go gelesen haben.

Die Suche nach rune ist genauso mehrdeutig wie String , daher sehe ich keinen eindeutigen Vorteil darin, es als kontextbezogenes Schlüsselwort zu verwenden.

Übrigens: Deshalb solltest du string verwenden und nicht String 😄

Übrigens: Aus diesem Grund sollten Sie string und nicht String verwenden

Was meiner Meinung nach zu 99 % der Grund dafür ist, dass die Leute ein Sprach-Keyword wollen. Die anderen 1% sind einfach "besser aussehen" 😏

Daumen runter für die starke Abneigung gegen das Schlüsselwort „Rune“.

Ein besseres Wort ist Glyphe, da es bereits das allgemeine Konzept eines elementaren Symbols in der Typografie darstellt.

Rune ist eine bestimmte Art von Glyphe, die ironischerweise von Unicode definiert wird. Go als Stand der Technik zu bezeichnen, ist etwas lächerlich. Der Stand der Technik für Runen ist das, was 150 n. Chr. Zurückgeschrieben wurde, und tatsächliche physische Runensteine. Nicht das, was jemand in Redmond für eine Rune hält. Der Versuch, bestehende Konzepte auf diese Weise neu zu definieren, ist ungewöhnlich, da .NET normalerweise über eine gut gestaltete API-Oberfläche verfügt. Dies ist eine seltene Ausnahme einer sehr schlechten API-Benennung, und ich möchte meine Unzufriedenheit zum Ausdruck bringen.

Ein besseres Wort ist Glyphe, da es bereits das allgemeine Konzept eines elementaren Symbols in der Typografie darstellt.

Das Problem ist, dass "Glyphe" ein verwendeter Begriff ist, wenn der Unicode in sichtbaren Text gerendert wird (von: utf8everywhere.org )

Glyphe

Eine bestimmte Form innerhalb einer Schriftart. Schriftarten sind Sammlungen von Glyphen, die von einem Schriftdesigner entworfen wurden. Es liegt in der Verantwortung der Textformungs- und Rendering-Engine, eine Folge von Codepunkten in eine Folge von Glyphen innerhalb der angegebenen Schriftart umzuwandeln. Die Regeln für diese Konvertierung können kompliziert und vom Gebietsschema abhängig sein und den Rahmen des Unicode-Standards sprengen.

Go als Stand der Technik zu bezeichnen, ist etwas lächerlich.

Unter Verwendung des Begriffs, den Rob Pike und Ken Thompson bei der Erstellung von Utf-8 verwendeten https://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt

Rob Pike arbeitet jetzt an Go, weshalb es den ursprünglichen Begriff verwendet.

Rune ist eine bestimmte Art von Glyphe, die ironischerweise von Unicode definiert wird.

Runic ist durch Unicode definiert, Rune nicht

Runic ist durch Unicode definiert, Rune nicht

Ich denke nicht, dass dies eine genaue Aussage ist, die neueste Unicode-Spezifikation (http://www.unicode.org/versions/Unicode11.0.0/UnicodeStandard-11.0.pdf) hat 37 Treffer für "rune" (nur 36 sind gültig , das letzte ist Teil eines größeren Wortes) und bezieht sich immer auf einzelne Buchstaben des Runenalphabets.

Ich denke nicht, dass dies eine genaue Aussage ist, die neueste Unicode-Spezifikation hat 37 Treffer für "Rune".

Im Haupttext, der die Motivationen beschreibt; nicht in einem Charakternamen oder Textblocknamen (wo sein Runen- und Runenzeichen)

Im Haupttext, der die Motivationen beschreibt; nicht in einem Charakternamen oder Textblocknamen (wo sein Runen- und Runenzeichen)

Okay, gerecht. Aber dann sind wir wieder bei dem Problem, dass die aktuelle Unicode-Spezifikation den Begriff "Rune" nicht definiert und wenn er verwendet wird, es sich um informativen Text handelt, der "Runenzeichen" beschreibt.

Was es formal definiert und zur Beschreibung von Dingen verwendet, ist "Code Point" und "Code Unit".

  • Auch wenn der/die ursprüngliche(n) Ersteller historisch gesehen den Begriff „Rune“ verwendet haben, tut dies die offizielle Spezifikation nicht (und ich könnte mir vorstellen, dass sie gute Gründe hatten, ihn nicht zu verwenden).

Muss kurz sein oder seine Verwendung wird hässlich

int CountCommas(string str)
{
    int i = 0;
    foreach(UnicodeCodePoint c in str.AsUnicodeCodePoints())
    {
        if (c == ',') i++;
    }
}

string Trim(string str)
{
    int end = str.Length - 1;
    int start = 0;

    for (start = 0; start < Length; start++)
    {
        if (!UnicodeCodePoint.IsWhiteSpace(str.GetUnicodeCodePointAt(start)))
        {
            break;
        }
    }

    for (end = Length - 1; end >= start; end--)
    {
        if (!UnicodeCodePoint.IsWhiteSpace(str.GetUnicodeCodePointAt(start)))
        {
            break;
        }
    }

    return str.SubString(start, end);
}

vs

int CountCommas(string str)
{
    int i = 0;
    foreach(Rune c in str.AsRunes())
    {
        if (c == ',') i++;
    }
}

string Trim(string str)
{
    int end = str.Length - 1;
    int start = 0;

    for (start = 0; start < Length; start++)
    {
        if (!Rune.IsWhiteSpace(str.GetRuneAt(start)))
        {
            break;
        }
    }

    for (end = Length - 1; end >= start; end--)
    {
        if (!Rune.IsWhiteSpace(str.GetRuneAt(start)))
        {
            break;
        }
    }

    return str.SubString(start, end);
}

Für die Länge würde ich mich total für CodePoint.IsWhiteSpace und str.GetCodePointAt , aber Rune macht auch Spaß und ich habe nichts dagegen.

@jnm2 Wir würden GetCodePointAt nicht verwenden, wenn es um Strings geht. Es ist zu zweideutig: Wir wissen nicht, ob Sie das char wollten, das sich zufällig an diesem Index befand (da alle char s - sogar ungepaarte Surrogate - auch gültige Codepunkte sind) oder den Skalar / Rune, die sich zufällig an diesem Index befand.

@GrabYourPitchforks Kann GetRuneAt das gleiche Problem vermeiden, oder meinst du, beides würde keinen Sinn machen?

@jnm2 Ich habe gerade gesagt, dass insbesondere CodePoint in diesem Szenario zu mehrdeutig ist. Andernfalls sollte der Methodenname GetXyzAt mit dem Typnamen Xyz übereinstimmen, der schließlich hineinkommt.

Zu Ihrer Information, die Core-Implementierung ist jetzt eingecheckt (siehe https://github.com/dotnet/coreclr/pull/20935). Geben Sie ihm etwas Zeit, um es an Corefx weiterzugeben, dann kommen die Referenz-APIs über https://github.com/dotnet/corefx/pull/33395 herein. Fühlen Sie sich frei, dieses Problem offen zu lassen oder es zu lösen, wie Sie es für richtig halten.

Ich erwarte nicht, irgendjemanden zu beeinflussen oder etwas ändern zu können, außer nur fürs Protokoll:

Ein besseres Wort ist Glyphe, da es bereits das allgemeine Konzept eines elementaren Symbols in der Typografie darstellt.

Das Problem ist, dass "Glyphe" ein verwendeter Begriff ist, wenn der Unicode in sichtbaren Text gerendert wird (von: utf8everywhere.org )

Diese Argumentationslinie unterstützt Rune auch nicht, da „Rune“ im Laufe der Geschichte seit über tausend Jahren ein verwendeter Begriff ist, lange bevor Unicode oder Transistoren oder Microsoft oder Open Source jemals existierten. Zumindest weist es darauf hin, dass einige willkürlich unterschiedliche Standards auf verschiedene Vorschläge anwenden, was offensichtlich nicht konsistent ist. Vielleicht geht es also eher darum, wer zuerst war oder am lautesten ist, als um das schlüssigste Argument, was weiß ich. Ich bin nur ein Nachzügler, der versucht, den Prozess zu verstehen, aber es ergibt keinen Sinn.

Go als Stand der Technik zu bezeichnen, ist etwas lächerlich.

Unter Verwendung des Begriffs, den Rob Pike und Ken Thompson bei der Erstellung von Utf-8 verwendeten https://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt

Rob Pike arbeitet jetzt an Go, weshalb es den ursprünglichen Begriff verwendet.

Go und Rob Pike sind relativ neue Leute in diesem Thema. Tatsächlich ist ihre Meinung in Bezug auf die Definition dessen, was eine Rune historisch und in der populären Literatur und Gesellschaft ist, etwas irrelevant. Rob hat keine Runensteine ​​selbst von Hand gehämmert, daher hat er nur wenige Qualifikationen, um zu definieren, was eine Rune ist. Ich wette, er kann nicht einmal selbst Runenschriften schreiben oder lesen, aber das ist meine Vermutung. Er kann dieses Konzept bestenfalls durch Codierung erfassen, aber er kann nicht hereinkommen und sagen, dass ein chinesisches Schriftzeichen, eine arabische Schrift oder Hangul oder ein Smiley eine Rune ist oder was auch immer ein "Code Point" ist, jetzt auch eine Rune ist. oder etwas ähnliches. Es scheint fast respektlos auf dem Begriff herumzutrampeln, schau, jetzt kann alles eine Rune sein, was bedeutet, dass Runen nichts anderes als ein Platzhalterbegriff mit vier Buchstaben sind, um sich auf etwas Esoterisches im Bereich der Textkodierung zu beziehen.

Rune ist eine bestimmte Art von Glyphe, die ironischerweise von Unicode definiert wird.

Runic ist durch Unicode definiert, Rune nicht

Unicode soll nicht neu definieren, was eine Rune oder Rune ist. Wenn sie das tun, überschreiten sie ihr Mandat. Sie haben nichts damit zu tun, der Öffentlichkeit zu sagen, was eine Rune ist. Tatsächlich haben sie nichts damit zu tun, irgendeine neue Sprache oder ein Zeichensystem zu definieren. Sie können sich nicht einfach ein Wort aneignen, das schon seit tausend Jahren ein deutlich überladener Begriff ist, und dann jubelnd herumlaufen, als hätten sie ein neues Konzept erfunden. Die Runenschrift besteht nur aus Runen, und Runen sind bereits ein etabliertes Konzept. Wenn Sie eine zufällige Person auf einer Straße fragen, was eine Rune ist, wird sie nicht an Unicode denken.

Zusätzlich zu all den oben genannten Problemen ist Rune eine schlechte Metapher, was das Schlimmste ist. Es erklärt nichts. Es fügt nur eine weitere Ebene der Verwirrung hinzu. Jeder Neueinsteiger in das Thema muss jetzt eine Begriffsklärungs- und Leserunde durchlaufen, da jeder mit dem Kontext kommt, dass eine Rune ein historisches Schriftsystem ist, das in bestimmten Kulturen verwendet wird. Die Erklärung muss etwa so lauten: "Eine Rune ist ein Unicode-Codepunkt". "Aber warum nicht Codepunkt nennen?" "Nun, weil es zu lang ist.", oder "Jemand hat entschieden, dass er Runen mag". Also im Grunde genommen, weil jemand denkt, dass 9 Buchstaben im Vergleich zu 4 zu viel sind (obwohl sie Autovervollständigung mit Intellisense haben und nichts im Vergleich zu Java Kingdom Of Nouns sind), müssen wir uns jetzt mit dieser Verwirrung auseinandersetzen und dies Tausenden erklären von Entwicklern, die sich möglicherweise mit Unicode beschäftigen müssen. Verwenden Sie einfach eine using-Anweisung, um den Begriff abzukürzen, wenn Sie ihn häufig im Code verwenden.

Es muss auch nicht UnicodeCodePoint sein, es kann einfach CodePoint sein. Das ist schon einmalig. Es gibt viele API-Begriffe, die länger als „CodePoint“ sind, das sollte also ausreichen. Wenn es immer noch zu lang ist, verwenden Sie einfach eine using-Anweisung mit einer Abkürzung.

Ich sehe voraus, dass dies zu einer dieser Gotcha-Interviewfragen wird, die wirklich nicht viel Wert hinzufügen oder eine logische Grundlage in irgendetwas Nützlichem haben. Zumindest für die Metapher „Meilenstein“, wo wir gerade bei Symbolwörtern sind, die in der Softwareentwicklung auf der Grundlage von Stein und Fels abgeleiteten Begriffen verwendet werden, hat ein Meilenstein eine echte beschreibende Bedeutung. Es kommuniziert sofort ein Konzept, das jeder kennt. Aha, ein Meilenstein, wie wenn man auf einer langen Reise unterwegs ist und auf dem Trail vorbeikommt. Es ist eine schöne Metapher aus der realen Welt, die tatsächlich hilft, etwas zu visualisieren und sofort zur Managementsprache werden kann. Ich kann mir nicht vorstellen, dass Leute auf diese Weise über Runen sprechen, es sei denn, sie sind mit dem Thema bestens vertraut, und an diesem Punkt wissen sie bereits, dass es nur ein Gimmick-Begriff für Codepunkt ist.

Ein besseres Wort ist Glyphe, da es bereits das allgemeine Konzept eines elementaren Symbols in der Typografie darstellt.

Das Problem ist, dass "Glyphe" ein verwendeter Begriff ist, wenn der Unicode in sichtbaren Text gerendert wird (von: utf8everywhere.org)

Diese Argumentationslinie unterstützt Rune auch nicht, da „Rune“ im Laufe der Geschichte seit über tausend Jahren ein verwendeter Begriff ist, lange bevor Unicode oder Transistoren oder Microsoft oder Open Source jemals existierten.

Mein Punkt war, dass das Wort "Glyphe" problematisch ist, da es bereits als eines der Konzepte beim Rendern von Text verwendet wird. es ist die grafische Darstellung dieses Zeichens in einer bestimmten Schriftart. Ein Zeichen kann also durch viele verschiedene Glyphen dargestellt werden.

... wieder mit @benaadams mit dem 10.000-Meter-Blick auf die Dinge und der richtigen Antwort 😁

Ehrlich gesagt müssen wir mit dem alten Sprichwort leben: „Man kann einige Menschen die ganze Zeit glücklich machen und alle Menschen manchmal glücklich machen, aber man kann nicht alle Menschen immer glücklich machen die Zeit." Dies ist sehr viel eine Situation der ersteren.

Siegel?

Exit, pursued by a bear.

Als jemand, der diese API ausgiebig nutzen würde, gebe ich eine starke Stimme für Code Point ab. Die Unicode-Terminologie ist bereits verwirrend genug, und es gibt bereits viele Inkonsistenzen. Sie werden mein Leben viel einfacher machen, wenn ich überall nur „Code Point“ sagen kann.

Ich liege gerade im Bett. Wenn ich mich seitwärts drehe, stehe ich vor einem Whiteboard, das an meiner Wand lehnt. Seit Monaten beherbergt dieses Whiteboard verschiedene Kritzeleien und Diagramme, während ich versuche herauszufinden, wie ich in C# effizient mit IDNs umgehen kann. Ich behandle es wie ein Relikt, das ich aus den Tiefen der Hölle beschworen habe. Wenn ich versuchen würde, die darin beschriebene Logik zu erklären, könnte ich es nicht.

Bitte machen Sie mir das Leben nicht schwerer. Ein Codepunkt ist ein Codepunkt. Es ist keine Rune, keine Glyphe, kein Zeichen, kein Graphem oder gar ein Symbol. Es muss für einen Menschen nichts Bedeutungsvolles darstellen – es könnte ein Kontrollcode sein. Es ist möglicherweise kein visuelles Symbol, wie der Name „Rune“ impliziert. Es ist nur ein Codepunkt.

Ein konkreteres Argument ist, dass „Rune“ die Darstellung eines einzelnen Graphems impliziert, was sehr oft nicht der Fall ist. Wenn ich die Anzahl der Codepunkte und die Anzahl der Grapheme zähle, erhalte ich möglicherweise zwei sehr unterschiedliche Zahlen. Dieselbe Folge von Graphemen könnte durch zwei unterschiedliche Reihen von Codepunkten dargestellt werden.

Ein besseres Wort ist Glyphe, da es bereits das allgemeine Konzept eines elementaren Symbols in der Typografie darstellt.

Das ist noch schlimmer. Ein einzelner Codepunkt könnte durch mehrere Glyphen dargestellt werden, und ein einzelnes Glyph könnte mehrere Codepunkte darstellen. Die genaue Zuordnung kann je nach System, Programm, Schriftart ... variieren.

Alle diese Wörter haben sehr spezifische technische Bedeutungen. Während die Unterschiede im Zusammenhang mit diesem Vorschlag unbedeutend erscheinen mögen, haben sie anderswo echte Konsequenzen, insbesondere in anderen Sprachen als Englisch.

Nur als Beispiel dafür, wie schwierig es sein kann, mit Text umzugehen, selbst in einer so verbreiteten Sprache wie Deutsch:

  1. Wandeln Sie ß in Großbuchstaben um und Sie erhalten SS .
  2. Konvertieren Sie es wieder in Kleinbuchstaben und Sie erhalten ss .

Probleme:

  • Was soll char.ToUpper('ß') zurückgeben? (Es muss ein einzelnes Zeichen zurückgegeben werden.)
  • Unicode 5.1 wurde eine Großbuchstabenversion von ß hinzugefügt, die mein Telefon nicht in dieses Textfeld eingeben kann. Wenn ich versuche, es einzufügen, bekomme ich SS. Jetzt sind obere/untere Konvertierungen noch zweideutiger.
  • Das Ändern der Hülle einer Saite ändert ihre Länge.
  • Falländerungen sind nicht idempotent oder reversibel.
  • Sie können keinen Vergleich ohne Berücksichtigung der Groß-/Kleinschreibung durchführen, indem Sie einfach jede Zeichenfolge klein schreiben.

Auch wenn dies kein direktes Beispiel für eine Situation ist, in der Terminologie Probleme verursacht, zeigt es doch, dass es Grenzfälle gibt, an die wir normalerweise nicht denken. Jedem Begriff eine eindeutige, konsistente Bedeutung zu geben, hilft Programmierern, diese Probleme zu kommunizieren. Wenn ich einen Teamkollegen bitte, eine Funktion zum Zählen von Graphemen zu schreiben, weiß er genau, was er zählen wird und wie es geht. Wenn ich sie wieder auffordere, Codepunkte zu zählen, wissen sie genau, was zu tun ist. Diese Definitionen sind unabhängig von den Sprachen und Technologien, die wir verwenden.

Wenn ich einen JavaScript-Entwickler bitte, Runen zu zählen, werden sie mich ansehen, als hätte ich drei Köpfe.

Wikipedia sagt

Unicode definiert einen Codespace von 1.114.112 Codepunkten im Bereich von 0hex bis 10FFFFhex

Codepoint scheint der offizielle Name zu sein. Ich habe diesen Thread gelesen und kein zwingendes Argument dafür gefunden, warum der Codepunkt falsch wäre.

Ich stimme zu, dass Codepunkt hier nicht der richtige Begriff ist. Zumindest enthält er laut Unicode-Standard keine Werte über 10FFFF (http://unicode.org/glossary/#code_point).

Vielleicht ist der Satz einfach falsch? Es sagt "jeder Wert im Coderaum". Es bedeutet also eindeutig alles, während gleichzeitig die ganze Zahl falsch wird.

Außerdem hat "Rune" eine reale Bedeutung , die nichts mit Unicode zu tun hat. In Deutschland hat das Wort "Rune" nationalsozialistische Konnotationen, weil Runen eine "germanische" Geschichte haben, auf die sich die Nazis gerne bezogen.

Ich finde "Rune" ein verwirrender Name. Mag hier jemand wirklich "Rune" oder basieren die Argumente dafür auf Korrektheit? Intuitiv ist es ein wirklich schlechter Name.

Vielleicht ist der Satz einfach falsch? Es sagt "jeder Wert im Coderaum". Es bedeutet also eindeutig alles, während gleichzeitig die ganze Zahl falsch wird.

Dieser Satz ist richtig. Der Coderaum reicht von U+0000 bis U+10FFFF. Unicode könnte theoretisch eines Tages darüber hinaus erweitert werden, aber es würde UTF-8 und UTF-16 kaputt machen. Wir bräuchten neue Kodierungen.

Bearbeiten: Zitieren Sie mich eigentlich nicht zum Bruch von UTF-16, aber ich bin mir ziemlich sicher, dass es UTF-8 brechen würde. UTF-8 kann 0xFFFFFF (2^24 -1) definitiv nicht darstellen.

Bearbeiten 2: Zur Verdeutlichung gibt Unicode an, dass Codepunkte niemals U + 10FFFF überschreiten können. Das bedeutet nicht, dass es derzeit 0x110000 Codepunkte gibt – die meisten dieser Codepunkte sind nicht zugewiesen.

@Zenexer @GSPP

Dieser Typ, wie er derzeit in master ( System.Text.Rune ) eingecheckt ist, bildet sehr spezifisch auf einen "Unicode-Skalarwert" ab ( siehe Glossar ). Die ctors des Typs lösen eine Ausnahme aus, wenn Sie versuchen, ihn aus den Werten -1 , 0xD800 oder 0x110000 zu konstruieren, da dies gemäß der Unicode-Spezifikation keine Skalarwerte sind. Wenn Sie einen Rune -Parameter als Eingabe für Ihre Methode verwenden, müssen Sie dafür keine Validierungsprüfung durchführen. Das Typsystem hat bereits sichergestellt, dass es aus einem gültigen Skalarwert konstruiert wurde.

Betreff: Konvertierung von Groß- und Kleinschreibung: Alle APIs für die Konvertierung von Groß- und Kleinschreibung in .NET Framework verwenden _sofern nicht anders angegeben_ eine Technik, die als einfache Fallfaltung bezeichnet wird. Unter den Regeln für die einfache Groß-/Kleinschreibung wird für jeden Eingabe-Skalarwert auch garantiert, dass die Klein-, Groß- und Titelform der Ausgabe jeweils genau ein Skalarwert ist. (Einige Eingaben, wie die Ziffern 0-9 oder Satzzeichen, haben keine Einträge in der Fallumwandlungskarte. In diesen Fällen geben Operationen wie _ToUpper_ einfach den Eingabeskalarwert zurück.) Außerdem unter einfachen Fallfaltungsregeln, wenn die Eingabe ist in der Basic Multilingual Plane (BMP), dann muss die Ausgabe auch in der BMP erfolgen; und wenn der Eingang in einer zusätzlichen Ebene liegt, muss der Ausgang auch in einer zusätzlichen Ebene liegen.

Daraus ergeben sich einige Konsequenzen. Erstens geben Rune.ToUpper und seine Freunde immer einen einzelnen _Rune_ (Skalar)-Wert zurück. Zweitens geben String.ToUpper und Co. immer einen String mit genau derselben Länge wie seine Eingabe zurück. Dies bedeutet, dass eine Zeichenfolge, die „ß“ (miniscules eszett) enthält, nach einer Groß-/Kleinschreibung je nach verwendeter Kultur möglicherweise „ß“ (keine Änderung) oder „ẞ“ (majuscule eszett) enthält. Aber es _wird kein_ "SS" enthalten, da dies die Länge der Zeichenfolge ändern würde, und fast alle öffentlich zugänglichen .NET-APIs zur Konvertierung von Groß- und Kleinschreibung verwenden einfache Faltregeln für Groß- und Kleinschreibung. Drittens ist es _nicht_ garantiert, dass Utf8String.ToUpper und seine Freunde (noch nicht eingecheckt) einen Wert zurückgeben, dessen _Length_-Eigenschaft mit der _Length_-Eigenschaft des Eingabewerts übereinstimmt. (Die Anzahl der UTF-16-Codeeinheiten in einer Zeichenfolge kann sich nach der einfachen Fallfaltung nicht ändern, aber die Anzahl der UTF-8-Codeeinheiten in einer Zeichenfolge kann sich ändern. Dies liegt daran, wie BMP-Werte von UTF-16 und UTF- 8.)

Es gibt einige .NET-APIs, die intern komplexe Fallfaltungsregeln anstelle einfacher Fallfaltungsregeln verwenden. String.Equals , String.IndexOf , String.Contains und ähnliche Operationen verwenden abhängig von der Kultur komplexe Faltregeln für Groß- und Kleinschreibung unter der Decke. Wenn Ihre Kultur also auf _de-DE_ eingestellt ist, werden die aus einem Zeichen bestehende Zeichenfolge „ß“ und die aus zwei Zeichen bestehende Zeichenfolge „SS“ als gleich verglichen, wenn Sie _CurrentCultureIgnoreCase_ übergeben.

@GrabYourPitchforks Ich widerspreche in erster Linie der Namenswahl. Das Casefolding-Beispiel sollte nur betonen, wie kompliziert Unicode (und Text im Allgemeinen) sein kann. Solange es eine Möglichkeit gibt, mit normalization umzugehen , ist es mir egal, wie die einfachen Operationen funktionieren, da ich für meinen Anwendungsfall sowieso für alles in NFKD konvertieren werde.

Dieser Satz ist richtig. Der Coderaum reicht von U+0000 bis U+10FFFF. Unicode könnte theoretisch eines Tages darüber hinaus erweitert werden, aber es würde UTF-8 und UTF-16 kaputt machen. Wir bräuchten neue Kodierungen.

Nur um pingelig zu sein (oder falls es die Leute interessiert): Theoretisch funktioniert der UTF-8-Algorithmus für bis zu 42 Bit (Präfix-Byte 0xFF und 7 Byte mit 6-Bit-Nutzlast), und ursprünglich deckten die ersten Spezifikationen die vollen 31 ab Bitraum dieser alten Versionen des universellen Zeichensatzes (UCS4) - die aktuellen Spezifikationen (RFC 3629, Unicode-Standard, Anhang D von ISO/IEC 10646) stimmen jedoch alle darin überein, ihn auf den aktuellen Bereich gültiger Codepunkte (U+ 0000 bis U+10FFFF).

Bei UTF-16 ist die Situation schwieriger. Aber sie könnten Codepunkte in einer oberen Ebene als "Escapes" für 32 Bit oder mehr reservieren. Da die Flugzeuge 3 bis 13 derzeit nicht definiert sind, könnten sie zwei davon als "niedriges Ersatzflugzeug" und "hohes Ersatzflugzeug" reservieren. Dann würde ein 32-Bit-Codepunkt in zwei 16-Bit-Werte aufgeteilt (einer in jeder Ebene), und dann würde jeder Wert mit zwei "klassischen" Ersatzwerten codiert, wobei effektiv 4 Codeeinheiten mit jeweils 16 Bit verwendet würden, um einen 32-Bit-Codepunkt zu codieren.

Übrigens, AFAICS, das Unicode-Konsortium hat öffentlich erklärt, dass es niemals Codepoints über U+10FFFF zuweisen wird, also hoffe ich, dass ich in der Praxis lange im Ruhestand sein werde, bevor das tatsächlich passiert. :zwinkern:

Dieser Typ, wie er derzeit in den Master eingecheckt ist ( System.Text.Rune ), wird sehr spezifisch einem "Unicode-Skalarwert" zugeordnet.

@GrabYourPitchforks danke für diese Klarstellung. Das bedeutet, dass die Struktur keinen Codepunkt darstellt. Dieser Name wäre also tatsächlich falsch.

Ich schätze, UnicodeScalar ist als Name zu geheimnisvoll...

@GrabYourPitchforks , was bleibt für dieses Problem zu tun?

@stephentoub Es sind keine zusätzlichen Funktionen für den In-Box-Typ Rune für 3.0 geplant, aber @migueldeicaza hatte Ideen zur Erweiterung der Reichweite des Typs, einschließlich für Dinge wie Graphem-Cluster. (Das nächste, was wir in der Box haben, ist TextElementEnumerator , was ein sehr veralteter Typ ist.) Einige dieser Ideen wurden in diesem Thread verbreitet, aber es gibt noch nichts Konkretes.

Wir könnten dieses Problem offen lassen, falls die Community die Szenarien weiter diskutieren möchte, oder wir könnten die Leute anweisen, neue Probleme zu eröffnen, wenn sie spezifische Vorschläge machen möchten. TBH Ich habe keine starke Präferenz.

Danke. Da Rune bereits eingeführt wurde und die hier skizzierten APIs (oder Annäherungen davon) bereits verfügbar gemacht wurden, schließen wir dies. Zusätzliche Unterstützung kann über separate Probleme adressiert werden.

Ist dies an dieser Stelle also im Wesentlichen stabilisiert? Denn ganz ehrlich, dieser schreckliche Name, der nicht mit Informationen übereinstimmt, die Sie über Unicode aus guten und genauen Quellen finden, und der die unglückliche Nuance hat, eine Glyphe im Gegensatz zu einem nicht druckbaren Zeichen zu implizieren, wird nur verschlechtern das bereits schreckliche Verständnis von Unicode durch Ihren durchschnittlichen Programmierer.

Ich weiß, dass dies zu diesem Zeitpunkt integriert wurde, aber ich möchte mich nur auf den Rune -Teil und die Meinungsverschiedenheit einiger Leute über den Namen einlassen.

Ich bin Rune zum ersten Mal in Plan 9 begegnet, und wie andere es in Go und anderen gesehen haben. Als die msdocs anfingen, Rune aufzulisten, wusste ich vor dem Lesen genau, was es war.

In mindestens zwei Fällen, Plan 9 und Go, verwenden die für UTF-8 verantwortlichen Personen den Namen Rune . Ich denke, man kann mit Sicherheit sagen, dass sie über diese Bedenken bereits nachgedacht haben und immer noch dachten, Rune sei angemessen. Runic ist nicht mehr wirklich ein gebrauchtes Schriftsystem, außer bei einigen Traditionalisten. Und Rune bedeutet das Graphem in diesem System, genau wie es hier im Wesentlichen das Graphem bedeutet (außer in Fällen wie Steuerzeichen.

Ich sehe wirklich wenig falsch mit der Namensgebung. Runic ist ein so altes Schreibsystem, dass ich sehr bezweifle, dass Ihr durchschnittlicher Programmierer es verwirren wird, und es gibt bereits einen mehrere Jahrzehnte alten De-facto-Standard von Rune für richtige Unicode-"Zeichen".

@Entomie

genauso wie es hier im Wesentlichen das Graphem bedeutet (außer in Fällen wie Steuerzeichen.

Das stimmt einfach nicht. Unicode enthält eine große Anzahl vorkomponierter Codepunkte, die mehrere Grapheme darstellen (im Allgemeinen Buchstaben- und diakritische Kombinationen), und diese werden häufig zum Schreiben von Sprachen wie Französisch und Spanisch verwendet, und so ziemlich der gesamte computerisierte Text in diesen Sprachen verwendet diesen Code Punkte.

Umgekehrt, selbst wenn ein einzelner Codepunkt ein Graphem darstellt, ist es sehr üblich, dass sie sich zu einem _Graphem-Cluster_ kombinieren, was für die korrekte Handhabung von Text in den meisten indischen Sprachen unerlässlich ist. So entspricht ein einzelnes Zeichen, wie es vom Benutzer wahrgenommen wird, wenn er sich mit den Pfeiltasten bewegt, oft mehreren Codepunkten in Folge. Es kann also keine einfache Entsprechung zwischen Codepunkten und Graphemen oder Graphemclustern geben. Selbst „Charakter“ wäre wahrscheinlich ein besserer Name, wenn man bedenkt, dass Programmierer daran gewöhnt sind, Charaktere an dieser Stelle als seltsam und verrückt zu betrachten, während „Rune“ den Eindruck erweckt, dass das Problem, die vom Benutzer wahrgenommenen Zeichengrenzen herauszufinden, für den Programmierer gelöst wurde schon, wenn es tatsächlich nicht gewesen ist.

Als die msdocs anfingen, Rune aufzulisten, wusste ich vor dem Lesen genau, was es war.

Die Tatsache, dass Sie dachten, dass der Name Rune Grapheme gut beschreibe, ist ein sehr guter Beweis für das Problem, das ich hier habe: Der Name „Rune“ gibt Programmierern ein falsches Sicherheitsgefühl, indem er es einfacher macht, anzunehmen, dass es eine solche Entsprechung gibt.

In mindestens zwei Fällen, Plan 9 und Go, verwenden die für UTF-8 verantwortlichen Personen den Namen Rune .

So sehr ich Ken Thompson und Rob Pike auch respektiere, ihre Arbeit hier bestand im Wesentlichen nur darin, ein sehr cleveres Schema zum Codieren einer Reihe von Ganzzahlen variabler Länge zu entwickeln. Sie sind keine Experten für Unicode als Ganzes, und ich stimme ihnen in dieser Frage ziemlich nicht zu. Ich gebe zu, dass ich auch kein Unicode-Experte bin, aber ich glaube nicht, dass der Appell an Autorität hier so stark ist, wie es scheinen mag.

und es gibt bereits einen mehrere Jahrzehnte alten De-facto-Standard von Rune für richtige Unicode-"Zeichen".

„Standard“ sagst du? Es waren hauptsächlich diese beiden, die den Namen vorangetrieben haben, und ein paar kleinere Programmiersprachen wie Nim, die ihn von Go übernommen haben. Und natürlich muss ich noch einmal wiederholen, dass ein Codepunkt kein einzelnes „richtiges Unicode-Zeichen“ darstellt, sei es im Sinne von Auswahl, Pfeiltastenbewegung, Graphemen oder Graphem-Clustern.

... meint hier im Wesentlichen das Graphem ...

Ja, da es nicht genau, aber ungefähr nah genug ist. Grapheme, zumindest so wie sie in der Linguistik definiert sind, sind die orthografischen Komponenten, die ein Schriftsystem ausmachen und verwendet werden, um Phoneme auszudrücken. Das ist keine 1:1 Sache. In Silben und Logosilben kann ein einzelnes Graphem mehrere Phoneme darstellen, typischerweise ein Konsonant-Vokal-Paar. Umgekehrt weisen alphabetische Sprachen häufig Fälle auf, in denen mehrere Grapheme ein einzelnes Phonem darstellen, wie z. Dann kann man sich nicht einmal sprachübergreifend darauf einigen, ob ein Buchstabe wie 'á' ein eigener Buchstabe ist oder ein 'a' mit Akzent. Wir können nicht einmal in Jahrtausende alten Sprachen Konsistenz herstellen. Wir werden darüber hinaus keine vollkommen konsistente Addition haben, das ist die Codierung dieser.

Da Sie für eine extrem strenge Semantik plädieren, ist das, was UNICODE als "Graphem-Cluster" bezeichnet, in der Linguistik oft nur ein einzelnes Graphem. Ist dies ein ungültiger UNICODE? Nein. Bedeutet dies, dass UNICODE es umbenennen muss? Nein, warum? Weil Kontext. Felder haben ihren eigenen Jargon, und solange es keine Verschmelzung innerhalb eines einzelnen Feldes gibt, ist das kein Problem.

Ich finde den Namen nicht so wichtig. Msdocs ist klar darüber, was Rune in der Zusammenfassung ist. Wenn die Leute die Dokumente nicht lesen, ist das ihr eigenes Problem. Die Leute reagieren nicht vehement auf 'Stream' und sagen Unsinn wie "ach was, wenn die Leute denken, es ist ein kleiner Fluss, weil der schon den gleichen Namen hat!" Nein.

@Serentty @Entomy Sie könnten beide auch an der Klasse StringInfo interessiert sein, die das eigentliche Unicode-Konzept "erweiterte Graphem-Cluster" offenlegt. Der Typ StringInfo ist ziemlich alt und implementiert daher eine sehr alte Version des Unicode-Standards, aber es wird aktiv daran gearbeitet, ihn zu aktualisieren, damit er mit UAX #29, Sec.

Ja, da es nicht genau, aber ungefähr nah genug ist.

Ich denke, die Frage nach zusammengesetzten versus zerlegten Darstellungen macht dies unwahr. Wenn wir hier von der linguistischen Definition eines Graphems ausgehen, im Gegensatz zu irgendeiner computerbezogenen Definition, dann sind 한 und 한 die exakt gleiche Folge von Graphemen (drei Hangul Jamo, die die Silbe _han_ als die Segmente HAN darstellen), und Der erste ist jedoch nur ein Codepunkt, während der zweite eine Folge von drei ist.

Felder haben ihren eigenen Jargon, und solange es keine Verschmelzung innerhalb eines einzelnen Feldes gibt, ist das kein Problem.

Genau das ist auch mein Punkt. Unicode ist ein wirklich kompliziertes System mit eigener Terminologie, warum also versuchen, ihm irgendeinen halbgaren „intuitiven“ Begriff aufzuzwingen, wenn er nicht so genau passt? Codepunkte sind Codepunkte. Sie haben keine sprachliche Parallele, und der Versuch, intuitiv zu sein, während nur 75 % genau sind, ist ein Rezept für die gleiche Art von Katastrophe, von der C# immer noch versucht, sich zu erholen.

Da Sie für eine extrem strenge Semantik plädieren, ist das, was UNICODE als "Graphem-Cluster" bezeichnet, in der Linguistik oft nur ein einzelnes Graphem.

Im Standard darf ein Cluster nur aus einem einzigen Graphem bestehen. Daran ist hier nichts auszusetzen. Ein _Cluster_ ist eine Einheit aus Textauswahl und Cursorbewegung.

Ich finde den Namen nicht so wichtig. Msdocs ist klar darüber, was Rune in der Zusammenfassung ist. Wenn die Leute die Dokumente nicht lesen, ist das ihr eigenes Problem.

Dies ist das „Programmierer müssen klüger sein“-Argument, das immer wieder vorgebracht wird, um schlechte Designentscheidungen zu verteidigen. Wenn Programmierer die Dokumentation lesen und lernen müssen, dass eine Rune sowieso ein Unicode-Codepunkt ist, was bringt es dann überhaupt, sie einen „intuitiveren“ Namen zu nennen? Das Argument hier scheint zu sein, dass „Codepunkt“ verwirrend ist, daher ist es sinnvoll, einen intuitiveren Namen zu wählen, aber wenn man dann mit dem Problem konfrontiert wird, dass der Name irreführend ist, ist die Verteidigung, dass Programmierer sowieso wissen sollten, was ein Codepunkt ist vom Lesen der Dokumentation. Wenn das der Fall ist, warum nennen Sie den Typ nicht einfach CodePoint und machen es Programmierern leichter, nachzuschlagen und sich darüber zu informieren? Dies alles lässt das Problem beiseite, dass die .NET-Dokumentation in Bezug auf Unicode in erster Linie ziemlich schrecklich ist und Ersatzpaare als nachträglichen Einfall in einer Welt von „16-Bit-Unicode-Zeichen“ behandelt.

Dies ist das „Programmierer müssen klüger sein“-Argument, das immer wieder vorgebracht wird, um schlechte Designentscheidungen zu verteidigen.

Ich habe das nie gesagt.

Das Argument hier scheint zu sein, dass „Codepunkt“ verwirrend ist

Ich habe das auch nie gesagt.

Die Leute reagieren nicht vehement auf 'Stream' und sagen Unsinn wie "ach was, wenn die Leute denken, es ist ein kleiner Fluss, weil der schon den gleichen Namen hat!" Nein.

Ich sage, dass Programmierer klug genug sind, nicht zu glauben, dass Rune speziell eine Runenrune ist, ähnlich wie sie wissen, dass Stream kein kleiner Fluss ist.

Lassen Sie mich das wiederholen

Ich sage, Programmierer sind schlau genug, das herauszufinden. Du legst mir Worte in den Mund.

Ich finde den Namen nicht so wichtig. Msdocs ist klar darüber, was Rune in der Zusammenfassung ist. Wenn die Leute die Dokumente nicht lesen, ist das ihr eigenes Problem.

Darauf beziehe ich mich hier. Das Argument für den Namen „Rune“ basiert auf Intuition und der intuitiven Verbindung mit der Vorstellung eines Graphems. Sie selbst haben argumentiert, dass die beiden nahe genug beieinander standen, dass es kein Problem war. Als ich auf all die Weisen hinwies, dass diese Intuition falsch war und die Korrespondenz sehr schlecht sein könnte, war Ihre Antwort im Wesentlichen, dass es keine Rolle spielt, weil Programmierer sowieso die Dokumentation lesen müssten. Das meine ich mit „Programmierer müssen klüger sein“. Dokumentation ist keine Entschuldigung für irreführende Namen, wenn es keinen althergebrachten Grund dafür gibt.

Ich sage, dass Programmierer klug genug sind, nicht zu glauben, dass Rune speziell eine Runenrune ist, ähnlich wie sie wissen, dass Stream kein kleiner Fluss ist.

Mein Argument hier ist nicht, dass die Leute es mit Runenrunen verwechseln werden. Mein Argument ist, dass die Leute es mit Glyphen, Graphemen und Graphem-Clustern verwechseln werden, die trotz Ihres Beharrens alle sehr schlecht mit Codepunkten korrelieren.

Ich sage, Programmierer sind schlau genug, das herauszufinden. Du legst mir Worte in den Mund.

Klug genug, um herauszufinden, dass es sich nicht um echte germanische Runen handelt, sicher. Aber um herauszufinden, dass es sich nicht um Glyphen, Grapheme oder Graphem-Cluster handelt? Meine tatsächliche Erfahrung mit der Qualität der meisten Programme, die mit Unicode umgehen, sagt nein.

Wenn die Leute die Dokumente nicht lesen, ist das ihr eigenes Problem.

Ja, und dazu stehe ich. Nicht aus Mangel an Intelligenz, sondern aus Neigung zu voreiligen Annahmen.

Wenn ein Programmierer annimmt, dass String ein starkes, dünnes Stück Seil bedeutet, das aus dem Verdrillen von Fäden besteht, denn ja, das bedeutet es, wird dies nicht als Problem mit dem Namen String angesehen .

Wenn ein Programmierer annimmt, dass Char ein verkohltes Material wie Holzkohle oder eine bestimmte Forellenart bedeutet, wird dies nicht als Problem mit dem Namen Char angesehen.

Wenn ein Programmierer davon ausgeht, dass character die Darstellung einer Reihe von mentalen und ethischen Merkmalen bedeutet, die beim Geschichtenerzählen verwendet werden, wird dies nicht als Problem mit dem Namen character angesehen.

Beachten Sie, dass dies alles Text-/Sprachangelegenheiten sind. Sie alle haben andere Bedeutungen. Und doch haben sich die Programmierer gut akklimatisiert. Diese Begriffe sind aufgrund einer etablierten Konvention in diesem Bereich zu De-facto-Standards geworden: unserem Jargon. Es gibt einen etablierten Präzedenzfall, dass Programmierer schlau genug sind, dem zu folgen.

Sie selbst haben argumentiert, dass die beiden nahe genug beieinander standen, dass es kein Problem war.

Ja, das ist GitHub. Zu einem bereits abgeschlossenen Thema, bei dem ich gerade meine Gedanken dazu hinzugefügt habe, warum ich der Meinung bin, dass Rune in Ordnung ist, weil es einen etablierten Präzedenzfall im Namen gibt. Dies ist weder der Ort noch der Kontext, um eine Abhandlung zu schreiben, die mit ausführlichen Definitionen und sorgfältig ausgewählten Wörtern gefüllt ist. Wenn ich beispielsweise eine PR für beispielsweise einen UTF-8-Decoder einfüge, werde ich nicht explizit beschreiben, warum ich den Hoehrmann-DFA gegenüber alternativen Ansätzen implementiert habe. Ich werde nur sagen "Hier ist es, hier ist ein Beweis, dass es funktioniert, hier sind einige Benchmarks, die belegen, warum ich mich dafür entschieden habe".

Mein Argument ist, dass die Leute es mit Glyphen, Graphemen und Graphem-Clustern verwechseln werden

Sie verwechseln weder die oben genannten noch Tree , Heap , Table , Key , Socket , Port ...

Dies ist eine äußerst unaufrichtige Argumentation. Ein Stück Faden und eine Textfolge sind nicht leicht zu verwechseln. Eine hohe Pflanze und eine Baumdatenstruktur sind nicht leicht zu verwechseln. Ein Codepunkt hingegen ist ein von den meisten Programmierern sehr schlecht verstandenes Konzept und wird ständig mit all den anderen Konzepten verwechselt, die wir besprochen haben. Die Lösung dafür ist, wie Sie sagen, das Lesen der Dokumentation. Eine Sprache, die ihren eigenen „klugen“ Namen für Codepunkte verwendet, macht es jedoch noch schwieriger, Wissen aus der _eigentlichen Unicode-Dokumentation_ auf diese Sprache anzuwenden. Und das bringt mich dazu:

Diese Begriffe sind aufgrund einer etablierten Konvention in diesem Bereich zu De-facto-Standards geworden: unserem Jargon.

Und das ist der Kern von allem. Sie scheinen zu behaupten, dass „Rune“ entweder ein etablierter Begriff für einen Codepunkt ist, der in der Programmierung weithin verstanden wird, oder es sein sollte. Wenn es ersteres ist, dann lade ich Sie ein, einen durchschnittlichen Programmierer mit Erfahrung in einer anderen wichtigen Programmiersprache als Go zu fragen, ob er sie jemals gehört hat. Wenn letzteres der Fall ist, würde ich Sie fragen, warum Sie in einer bereits verwirrenden und schlecht verstandenen Situation, die selbst von sehr erfahrenen Entwicklern häufig missverstanden wird, mit der offiziellen Unicode-Terminologie konkurrieren sollten.

@Entomy- Außenseiter-Eingabe: Ihr gesamtes Argument ist, soweit ich das beurteilen kann, "es ist verwirrend und schlecht, ja, aber es ist nicht so verwirrend und schlecht".
Damit? Warum kann es nicht stattdessen gut sein? Was ist das Problem damit, es genau so zu benennen, wie Unicode es benennt?
Außerdem sind Runen im allgemeinen Computerbereich keine Codepunkte oder gar Grapheme oder Cluster. Wenn Sie in Google nach „Unicode-Runen“ suchen, wird alles, was sie mit Codepunkten zu tun hat, erst auf Seite 2 angezeigt, und selbst dann sind es nur Godoc / Nim-Links. Sogar auf DuckDuckGo, mit dem Programmierer vielleicht bequemer sind, ist es immer noch ein Ergebnis auf Seite 2. Das einzige verbleibende Argument für den Namen, den ich gesehen habe, ist, dass es intuitiv ist, dass es einen Codepunkt darstellt, aber es ist nicht . Es ist intuitiv, dass es einen Graphem-Cluster oder vielleicht nur ein Graphem darstellt.
Quelle: Ich habe Go benutzt und dachte, es sei ein Graphem, bis ich vier Jahre später diese Ausgabe gerade las.

(und zu sagen, dass es in Ordnung ist, dass es ein Graphem vorschlägt, weil es "nah genug" ist, erinnert mich daran, dass das 16-Bit-Zeichen nah genug ist.)
Ja, wenn Programmierer schlauer wären und mehr Dokumentation lesen würden, bräuchten wir keinen aussagekräftigen Namen dafür oder überhaupt einen Typ. Die Leute würden nur wissen, dass sie Codepunkte in einem int-Rand anstelle von char übergeben müssen. Aber das sind sie nicht. Sie sind so schlau wie jetzt, und das wird sich nicht ändern, nur weil Yet Another API hinzugefügt wurde. Das Ziel ist es, die Menge an Software zu erhöhen , die andere Sprachen als Englisch korrekt handhabt, und nicht nur neue Möglichkeiten einzuführen, um dasselbe zu tun und dieselben Eintrittsbarrieren wie zuvor beizubehalten.

Nur aus Gründen der Argumentation und aus wissenschaftlichen Gründen möchte ich jeden hier auf die eine Programmiersprache hinweisen, die Unicode-Text am besten verarbeitet, wobei »am besten« durch »dem Unicode-Standard am nächsten kommen« definiert wird, nicht durch vorgetäuschte Einfachheit: Swift

  • String ist ein Puffer mit beliebigem Unicode-Text.
  • Character , über das Sie iterieren und was nicht, ist kein einzelner Unicode-Skalarwert, sondern ein Extended Grapheme Cluster. Siehe dieses Beispiel für den Graphem-Cluster : let decomposed: Character = "\u{1112}\u{1161}\u{11AB}" // ᄒ, ᅡ, ᆫ
  • Wenn Sie Unicode-Skalarwerte benötigen, können Sie diese ebenfalls durchlaufen. Ihr Typ heißt UnicodeScalar .
  • Und wenn Sie es wirklich brauchen, können Sie auch über UTF-8- und UTF-16-Codeeinheiten iterieren, was UInt 8 s und UInt 16 s ergibt.

Nun, ich schlage hier nicht vor, dass C# den vollen Swift-Stil übernimmt. Das wäre zwar erstaunlich, aber es sind auch verdammt viele Änderungen und Arbeit erforderlich. Ich bin hier, um vorzuschlagen, die Benennung im Swift-Stil aus all den Gründen, auf die @Serentty hingewiesen hat, aufzugreifen und die Option offen zu lassen, um Textzeichenfolgen schließlich in den Swift-Stil umzuwandeln.

Einige potentiell bessere Namen als Rune : CodeUnit32 , UnicodeScalar , CodeUnit , UniScalar , UnicodeValue , UniValue , UnicodeScalarValue . Ich denke, die ersten beiden passen gut in die Namenskonventionen von C#. Beachten Sie, dass UnicodeScalar objektiv der bessere Name ist, da Codeeinheiten nur Möglichkeiten sind, einen Unicode-Skalarwert im Unicode-Jargon zu codieren. CodeUnit32 impliziert also das Iterieren über die Codeeinheiten einer UTF-32-codierten Textzeichenfolge, während UnicodeScalar codierungsagnostisch ist.

Bearbeiten: Ja, der Name System.Rune ist bereits da draußen. All dies ist nur ein »wenn wir es besser machen wollen, bevor das Ding ein halbes Jahrzehnt alt ist«.

@Pie-Geschmack

Ihr gesamtes Argument ist, soweit ich das beurteilen kann, "es ist verwirrend und schlecht, ja, aber es ist nicht so verwirrend und schlecht".

Nein, das ist überhaupt nicht mein Argument. Ich tue das Beste mit meiner Behinderung, aber das ist nicht meine beabsichtigte Kommunikation.

Wenn Sie in Google nach „Unicode-Runen“ suchen, wird alles, was sie mit Codepunkten zu tun hat, erst auf Seite 2 angezeigt, und selbst dann sind es nur Godoc / Nim-Links.

Wenn Sie in Google nach „Unicode-String“ suchen, erfahren Sie auch nicht genau, wie .NET-Strings funktionieren. Hier geht es um die Suche nach einem angrenzenden Ding. Als sehr strenge Analogie programmiere ich sowohl in .NET als auch in Ada; string ist nicht dasselbe zwischen ihnen, und etwas Lektüre für jeden ist eine gute Idee.

Überladene Definitionen sind sprachlich nichts Ungewöhnliches, und doch kommen wir damit gut zurecht. Es mag Sie überraschen, aber „run“ hat mindestens 179 formale Definitionen, „take“ hat mindestens 127, „break“ hat mindestens „123“ und so weiter. [ Quelle ] Menschen sind erstaunlich fähig und können weitaus komplexere Situationen erfolgreich bewältigen, als das, was hier als problematisch angesehen wird. Die Sorge, dass "Rune" mindestens zwei formale Definitionen hat, ist meiner Meinung nach nicht gerechtfertigt, wenn gezeigt werden kann, dass Menschen mit über 50-facher Überlastung fertig werden.

Darüber hinaus wird das Verhalten von Suchmaschinen grob ausgenutzt. Bei den meisten Suchmaschinen erhalten Sie Ergebnisse basierend darauf, wie viele Seiten auf etwas verlinken. Es gibt auch andere Faktoren, wobei jeder Ansatz die Dinge anders gewichtet. Da .NET Rune im Vergleich dazu ein ziemlich neues Konzept ist, wird es viel weniger Inhalte geben, die darüber sprechen, und es wird mehr Seiten brauchen, um dorthin zu gelangen. Aber es verwendet auch das falsche Suchwerkzeug. Wenn ich Forschungsergebnisse zu Zeichenfolgensuchalgorithmen finden möchte, um zu sehen, ob in den letzten Jahren etwas Neues aufgetaucht ist, suche ich nicht bei Google oder DDG. Semantic Scholar, Google Scholar und andere sind bessere Ausgangspunkte. Wenn Sie etwas über .NET-APIs verstehen möchten, durchsuchen Sie zunächst MSDocs. Wenn ich mich darüber beschwere, dass "Trägheitsmoment", ein physikalischer / technischer Begriff, in seinem Namen vage oder irreführend ist und umbenannt werden sollte, weil ich in den ersten paar Büchern keine Informationen darüber finden kann, beginnend mit der niedrigsten Nummer in einer Bibliothek, die die Dewey-Dezimalklassifikation verwendet, ist dies kein Problem mit der Benennung von "Trägheitsmoment"; Ich suche eindeutig an der falschen Stelle.

Quelle: Ich habe Go benutzt und dachte, es sei ein Graphem, bis ich vier Jahre später diese Ausgabe gerade las.

Ich habe die Go-Dokumente und Versionshinweise durchgesehen, zumindest die, die ich finden konnte, und ich muss Ihnen zustimmen. Sie sind sehr vage darüber, was rune ist, und unglücklicherweise sind sie sogar vage darüber, wie groß rune ist. Ich vermute, dass diese Unbestimmtheit später Probleme verursachen wird, da ich gesehen habe, wie Ada in Bezug auf Datentypbeschränkungen ebenso ungenau war und sich Jahre später selbst in den Arsch beißt.

Ich muss jedoch sagen, dass msdocs mit einer sehr detaillierten und prägnanten Beschreibung einen viel besseren Job macht.

Stellt einen Unicode-Skalarwert dar ([ U+0000..U+D7FF ], einschließlich; oder [ U+E000..U+10FFFF ], einschließlich).

Abgesehen davon fehlen die Bemerkungen etwas und eine Erläuterung, warum Rune existiert und wann Sie es verwenden möchten, wäre von Vorteil (und auch der geeignete Ort für eine detailliertere Erklärung als meine vereinfachte oben erwähnte). . Ich werde dort einige Verbesserungen vorschlagen.

@Evrey

Nur aus Argumentationsgründen und aus wissenschaftlichen Gründen möchte ich jeden hier auf die eine Programmiersprache hinweisen, die Unicode-Text am besten verarbeitet

Dies ist eine Meinung. Dem stimme ich absolut zu; Swift handhabt modernes UNICODE sicherlich besser. Aber ohne ein Zitat von begutachteter, reproduzierbarer Forschung, die diese Ergebnisse bestätigt, ist dies keine wissenschaftliche Behauptung.

Nun, ich schlage hier nicht vor, dass C# den vollen Swift-Stil übernimmt. Das wäre zwar erstaunlich, aber es sind auch verdammt viele Änderungen und Arbeit erforderlich.

Und würde bestehende Software kaputt machen.

Lassen Sie die Option offen, um Textzeichenfolgen eventuell in den Swift-Stil umzuwandeln.

Und würde bestehende Software kaputt machen.

Ja, der Name System.Rune ist bereits da draußen. All dies ist nur ein »wenn wir es besser machen wollen, bevor das Ding ein halbes Jahrzehnt alt ist«.

Und würde bestehende Software kaputt machen.

Wenn Änderungen am vorhandenen Namen vorgenommen werden sollten, wie schlagen Sie hypothetisch vor, dass vorhandene Software, die auf .NET Core 3.0/3.1 abzielt, wo Rune bereits verwendet wird, weiterhin kompatibel ist, während sie auch als vorhanden ist ein anderer Name in späteren Ziellaufzeiten?

Und würde bestehende Software kaputt machen.

Wie bereits erwähnt, argumentiere ich nur aus der Perspektive des Prinzips und des Idealismus. Die Realität der Dinge ist reichlich erwähnt worden. Obwohl das alles eine Nuance hat:

  • Swift-Stil mit Strings zu gehen, macht nicht unbedingt die Software kaputt. Es ist lediglich eine Frage des Hinzufügens weiterer Aufzählungsmethoden und -typen zur bereits vorhandenen String -Schnittstelle. Damit meine ich nicht radikale Dinge wie das Ändern System.Char in einen Graphem-Cluster-Typ oder ähnliches.
  • Wenn ein bestehender Typname wie System.Char für einen ganz anderen Typ umfunktioniert würde, dann ja, das wäre eine große bahnbrechende Änderung. Und eine unverantwortliche Veränderung dazu. Da bin ich bei dir.
  • Ein hypothetischer .NET Core 4.0, in SemVer gesprochen, kann alles tun, was er will. Abgesehen davon sind die Änderungen bis zu einem hypothetischen 4.0 nicht so beängstigend: Verwandeln Sie System.Rune in einen veralteten Typ-Alias ​​für System.UnicodeScalar oder wie auch immer der Name lauten würde. Software, die Rune verwendet, wird keinen Unterschied bemerken, abgesehen von einem Verfallhinweis, und neue Software kann den besser benannten tatsächlichen Typ verwenden. Und eine hypothetische 4.0 lässt dann nur Rune fallen.
  • Ebenso könnte System.Char in einen Alias ​​für System.CodeUnit16 oder so umgewandelt werden.
  • Es im Swift-Stil zu machen, bedeutet dann effektiv nur, System.GraphemeCluster in die Mischung einzufügen.
  • Die Einführung weiterer, neuer Keyword-Aliase für all diese Typen kann problematisch sein.

Ich gebe hier nur Denkanstöße ab. Ich denke, dass System.Rune , obwohl es für seinen Zweck ein schlechter Typname ist, den vorherigen Namensstatus quo nicht wirklich verschlechtert. Ich finde es toll, dass es endlich einen richtigen Typ gibt, der alle Unicode-Skalare kodieren kann. Ich sehe jedoch eine gute Gelegenheit, einen Trend zu einer genaueren Handhabung und Benennung von Unicode zu verbreiten. Eine Gelegenheit, die sich jeder hier frei nehmen kann.

Hallo zusammen, der Name System.Text.Rune ist das, was versendet wurde und was wir in Zukunft verwenden. Es gab frühere erhebliche (und hitzige!) Diskussionen über die Verwendung des Namens UnicodeScalar anstelle von Rune , aber am Ende gewann Rune . Das Team hat derzeit nicht die Idee, einen anderen Namen dafür zu wählen. Und obwohl ich weiß, dass die Leute leidenschaftlich daran interessiert sind und wir das Gespräch hier weiter beobachten werden, sollten Sie sich letztendlich bewusst sein, dass sich jede Energie, die darauf verwendet wird, den Rechtsstreit über die Namensfrage fortzusetzen, nicht auszahlt.

Zur Verdeutlichung und gemäß den Dokumenten: Der Typ System.Text.Rune in .NET entspricht genau einem Unicode-Skalarwert. Dies wird durch die Konstruktion erzwungen. Dadurch ähnelt es eher dem Typ UnicodeScalar von Swift als dem Typ rune von Go.

Es wird versucht, einen Abschnitt zu den Rune -Dokumenten hinzuzufügen, in dem die Anwendungsfälle und die Beziehung zu anderen Textverarbeitungs-APIs in .NET und Konzepten in Unicode beschrieben werden. Das Tracking-Problem finden Sie unter https://github.com/dotnet/docs/issues/15845. Es gibt auch einen Link von diesem Tracking-Problem zu einem aktuellen Entwurf der Konzeptdokumentation.

Für mich ist der Hauptnachteil bei UnicodeScalar die große Diskrepanz zwischen der Länge des Typnamens und der Datengröße des Typs. Im Wesentlichen ist es ein int mit einigen Lücken in seiner Domäne.

Die Ausführlichkeit in der Verwendung wäre jedoch extrem:

foreach (UnicodeScalar unicodeScalar in name.EnumerateUnicodeScalars())
{
     // ... unicodeScalar contains 1 int
}

gegenüber dem Äquivalent char über string (und idealerweise würden die Leute den neuen Typ über char verwenden, da es sich um ganze Werte handelt und nicht um geteilte Werte)

foreach (char c in name)
{
     // ... c contains 1 ushort
}

Rune ist ein Kompromiss in der Ausführlichkeit von Typnamen:

foreach (Rune rune in name.EnumerateRunes())
{
     // ... rune contains 1 int
}

@GrabYourPitchforks

Hallo! Um ehrlich zu sein, bin ich nicht in diesen Streit verwickelt worden, weil ich versuche, die .NET-Leute davon zu überzeugen, dass der Name geändert werden muss, da das Schiff anscheinend ausgelaufen ist, sondern einfach, weil ich meine Meinung dazu äußern wollte andere in diesem Thread, die damit nicht einverstanden waren. Ich finde es wunderbar, dass C# endlich einen _echten_ Zeichentyp hat, im Gegensatz zu dem kaputten Zeichentyp, den es so lange hatte, und der Name ist völlig zweitrangig. Ich verstehe, dass zwischen Kürze und Genauigkeit ein großes Gleichgewicht gefunden werden muss, und obwohl ich den optimalen Punkt irgendwo um CodePoint gelegt hätte, verstehe ich, warum andere anderer Meinung sind.

Aber noch einmal möchte ich Ihnen für all die harte Arbeit bei der Modernisierung der Unicode-Unterstützung von .NET danken! Dies ist etwas, das für viele Menschen auf der ganzen Welt einen großen Unterschied macht.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen