Runtime: Introducción a System.Rune

Creado en 16 sept. 2017  ·  106Comentarios  ·  Fuente: dotnet/runtime

Inspirado por la discusión aquí:

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

Uno de los desafíos que enfrenta .NET con su soporte Unicode es que está arraigado en un diseño que hoy en día está obsoleto. La forma en que representamos los caracteres en .NET es con System.Char que es un valor de 16 bits, que es insuficiente para representar valores Unicode.

Los desarrolladores de .NET necesitan aprender sobre los arcanos pares sustitutos:

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

Los desarrolladores rara vez usan este soporte, principalmente porque no están lo suficientemente familiarizados con Unicode, y mucho menos con lo que .NET tiene para ofrecerles.

Propongo que introduzcamos un System.Rune que esté respaldado por un entero de 32 bits y que corresponda a un punto de código y que muestremos en C# el tipo equivalente rune para que sea un alias de este tipo.

rune se convertiría en el reemplazo preferido de char y serviría como base para el manejo adecuado de cadenas y Unicode en .NET.

En cuanto al por qué del nombre runa, la inspiración proviene de Go:

https://blog.golang.org/strings

La sección "Puntos de código, personajes y runas" proporciona la explicación, una versión corta es:

"Punto de código" es un poco complicado, por lo que Go introduce un término más corto para el concepto: runa. El término aparece en las bibliotecas y el código fuente, y significa exactamente lo mismo que "punto de código", con una adición interesante.

Actualización , ahora tengo una implementación de System.Rune aquí:

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

Con la siguiente 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
    }
}

Actualizar problemas conocidos

  • [x] Algunas API anteriores toman un uint, necesitan tomar una Runa.
  • [ ] Necesidad de implementar la familia IComparable
  • [ ] RuneCount/RuneLen necesita mejores nombres, consulte los documentos (¿deberían ser quizás Utf8BytesNeeded?)
  • [ ] Arriba, las API "ustring" hacen referencia a mi API UTF8, esto realmente no es parte de la API, pero debemos considerar si hay una puerta de enlace a System.String en algunas de ellas, o a Utf8String.
api-needs-work area-System.Runtime up-for-grabs

Comentario más útil

Lo dije en el número original y lo diré de nuevo. Abandonar lo que dice un estándar porque no le gusta la frase confundirá más de lo que resolverá y, dado que hay una página de códigos de runas en Unicode, eso solo lo confundirá más.

El nombre está mal.

Todos 106 comentarios

¿Espera que la representación en memoria sean cadenas de objetos de 32 bits o que se traduzcan sobre la marcha? ¿Qué pasa con la duplicación de la memoria si la primera? ¿Cuál es el impacto en el rendimiento si esto último?

¿Es una buena idea nombrar una tecnología relacionada con Unicode después de un script compatible con Unicode en particular (y una tecnología para mejorar el soporte del plano astral después de un script BMP)?

Creo que la propuesta (y tal vez deba hacerse más explícita) es que la representación en memoria de las cadenas no cambie en absoluto. El tipo Rune simplemente representa un punto de código individual distinto de 21 bits (almacenado como un int de 32 bits). Los métodos que se refieren a puntos de código podrían devolver potencialmente un Rune en su lugar. Presumiblemente hay alguna funcionalidad en string que le permitiría enumerar Rune .

Creo que hay un par de puntos obvios sobre los que necesitamos llegar a un consenso para algo como esto:

  1. ¿Existe un valor significativo en la creación de un tipo Rune en lugar de usar Int32 como lo hacen los métodos actuales?
  2. ¿Es la palabra "runa" realmente una buena elección?

Para responder (1), creo que necesitamos una descripción más completa de cómo se expondría Rune , qué métodos lo recibirían y lo devolverían, etc. Y para determinar si eso es mejor que tener que lidiar con Int32 en su lugar.

En cuanto a (2), yo mismo dudo un poco. "Runa" es una especie de palabra esotérica en inglés y tiene algunas connotaciones inusuales para su uso en este contexto. También está el punto que otros mencionan: choca con otro concepto de Unicode. Cuando hago una búsqueda de "Unicode Rune", obtengo principalmente resultados para el bloque Runic Unicode, y solo una pequeña documentación del idioma Go.

char es tanto media palabra como una palabra completa; y debe inspeccionar su entorno para determinar cuál, como la actual, representa la mitad de una letra o una letra completa.

Tal vez System.character donde siempre es una carta completa... :gafas de sol:

char es una representación un poco terrible e incluso para los idiomas ascii/latin solamente; el auge de los emoji seguirá presente; significa que char es un cheque y tal vez verifique el siguiente tipo char

@NickCraver en Twitter

Mientras que utf8 es una codificación de ancho variable; es raro (¿si es que lo hay?) que un usuario quiera tratar con medios caracteres; tanto para utf8 como para utf32.

Un tipo de 32 bits funcionaría bien para la enumeración.

Más difícil sería indexOf, Length, etc. para una perspectiva de rendimiento o memoria.

  1. la matriz de bytes es la mejor representación para un formato opaco; por ejemplo, mantener el formato en su formato original o en un formato final (transferencia de archivos, conexión por cable, etc.)
  2. la matriz de bytes es la mejor representación para el ancho de banda de la memoria y el tamaño de la memoria
  3. la matriz de bytes es consistente con Position e indexOf, Longitud, etc. en términos de bytes

Sin embargo, cuando comienza a preocuparse por los caracteres reales, mayúsculas, división en caracteres; entendiendo qué es un carácter, el byte se convierte en ancho variable. Char no hace que eso sea realmente mejor; duplica el tamaño de los caracteres más pequeños; incluye más caracteres, pero sigue siendo de ancho variable.

Para esto, un valor de 32 bits podría ser muy útil desde la perspectiva del código de usuario. Sin embargo, tiene problemas con la posición, la longitud y los elementos secundarios (indexOf, etc.)

Estoy muy interesado en una cadena solo ascii y una cadena utf8 "Implementación de cadena compacta" https://github.com/dotnet/coreclr/issues/7083; para el procesamiento rápido de cadenas solo ascii

Sin embargo, yendo en contra de todo lo que estaba argumentando allí... Me pregunto cómo sería una representación de 32 bits de utf8. La posición se asignaría a la posición; buscar caracteres sería rápido como lo es en ascii, los elementos están en tamaños nativos, etc. ¿cómo se compararía con el procesamiento de cada byte o carácter para determinar su tamaño?

La conversión hacia y desde sería más costosa; entonces sería más un formato de procesamiento; que un formato de almacenamiento.

@migueldeicaza , según tengo entendido, solo se refiere a expandir el formato de un solo carácter de 16 bits a 32 bits para que todas las representaciones estén contenidas en el valor; en lugar de la posibilidad de un valor medio, en lugar de necesariamente el formato interno.

Sin embargo, hay algunas cosas a considerar (es decir, relación de posición y costo de búsqueda, etc.)

Aparte: Swift también se ocupa de formatos de caracteres completos

Swift proporciona varias formas diferentes de acceder a las representaciones Unicode de cadenas. Puede iterar sobre la cadena con una instrucción for-in para acceder a sus valores de caracteres individuales como grupos de grafemas extendidos de Unicode. Este proceso se describe en Trabajar con personajes.

Alternativamente, acceda a un valor de cadena en una de las otras tres representaciones compatibles con Unicode:

  • Una colección de unidades de código UTF-8 (a las que se accede con la propiedad utf8 de la cadena)
  • Una colección de unidades de código UTF-16 (a las que se accede con la propiedad utf16 de la cadena)
  • Una colección de valores escalares Unicode de 21 bits, equivalente a la forma de codificación UTF-32 de la cadena (a la que se accede con la propiedad unicodeScalars de la cadena)

Lo dije en el número original y lo diré de nuevo. Abandonar lo que dice un estándar porque no le gusta la frase confundirá más de lo que resolverá y, dado que hay una página de códigos de runas en Unicode, eso solo lo confundirá más.

El nombre está mal.

@mellinoe

Rune proporcionaría muchas de las operaciones que hoy espera en un Char, como ToLower[Invariant], ToUpper[Invariant], ToTitle, IsDigit, IsAlpha, IsGraphic, IsSymbol, IsControl.

Además, proporcionaría cosas como:

  • EncodeRune (codifica una runa en un búfer de bytes)
  • RuneUtf8Len (devuelve el número de bytes necesarios para codificar la runa en UTF8),
  • IsValid (no todos los valores Int32 son válidos)

E interoperabilidad con cadenas y Utf8string según sea necesario.

Porté/ajusté el soporte de cadenas Go a .NET, y ofrece una vista de cómo se vería este mundo (esto es sin ninguna ayuda de tiempo de ejecución):

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

@benaadams dijo:

Me pregunto cómo sería una representación de 32 bits de utf8. La posición se asignaría a la posición; buscar caracteres sería rápido como lo es en ascii, los elementos están en tamaños nativos, etc. ¿cómo se compararía con el procesamiento de cada byte o carácter para determinar su tamaño?

UTF8 es una representación en memoria, que continuaría existiendo y seguiría siendo la representación (y con suerte, esta es la codificación interna a más largo plazo para cadenas futuras en .NET).

Descodificaría las cadenas UTF16 existentes (System.String) o las próximas cadenas UTF8 (Utf8String) no en Chars (por la razón en la que tanto usted como yo estamos de acuerdo), sino en Runas.

Algunos ejemplos, convertir una cadena Utf8 en runas:

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

¿Una cadena utf8 contiene una runa:

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

Me acabo de dar cuenta de que no implementé el indexador ("Consígueme la runa n-ésima")

La velocidad de acceso a la Runa N en una cadena es una función del almacenamiento, no de la Runa en sí. Por ejemplo, si su almacenamiento es UTF32, tiene acceso directo a cada runa. Esto es académico, ya que nadie usa eso. El acceso al elemento N en UTF16 y UTF8 requiere el escaneo adecuado de los elementos que componen la cadena (bytes o enteros de 16 bits) para determinar el límite correcto. No debe confundirse con String[int n] { get; } que solo devuelve el carácter n, independientemente de si es correcto.

@benaadams The Swift Character está un nivel más alto que una runa. Los caracteres en swift son "grupos de grafemas extendidos" que se componen de una o más runas que cuando se combinan producen un carácter legible por humanos.

Entonces, el carácter Swift no tiene un tamaño fijo de 32 bits, es de longitud variable (y también deberíamos tener esa construcción, pero pertenece a un tipo de datos diferente). Aquí está el ejemplo de esa página, pero esto también se extiende a la configuración del tono de un emoji:

Aquí hay un ejemplo. La letra é se puede representar como el único escalar Unicode é (LETRA E MINÚSCULA LATINA CON AGUDA, o U+00E9). Sin embargo, la misma letra también se puede representar como un par de escalares: una letra e estándar (LETRA E MINÚSCULA LATINA, o U+0065), seguida del escalar COMBINACIÓN DE ACENTO AGUDO (U+0301). El escalar COMBINING ACUTE ACCENT se aplica gráficamente al escalar que lo precede, convirtiendo una e en una é cuando se representa mediante un sistema de representación de texto compatible con Unicode.

Solo para mí, la palabra grapheme sería más autodescriptiva.

Mis dos centavos sobre el nombre, citando nuevamente la publicación de Go sobre cadenas con énfasis:

" Punto de código " es un poco complicado, por lo que Go introduce un término más corto para el concepto: runa. El término aparece en las bibliotecas y el código fuente, y significa exactamente lo mismo que "punto de código" , con una adición interesante.

Estoy 100% de acuerdo con @blowdart , llamarlo runa es confuso e incorrecto. El estándar Unicode menciona los puntos de código tres veces solo en la primera página del capítulo de introducción, pero el término runa no aparece en ninguna parte.

Si es un punto de código, entonces debería llamarse punto de código , así de simple.

Si el término runa nunca apareció en el estándar, podría estar bien, el problema es que aparece varias veces en el capítulo 8, en relación con las runas. No solo está mal, está confundiendo activamente el asunto con otro.

Solo para mí, la palabra grapheme sería más autodescriptiva.

Si se trata de puntos de código de 32 bits, el término grapheme sería confuso porque un grafema es otra cosa.

A menudo he querido un tipo de datos de punto de código (no en mucho tiempo, ya que lo que he trabajado ha cambiado, pero hace unos años quería mucho esto y escribí soluciones parciales superpuestas a partes de esa necesidad y podría haberlo hecho con una biblioteca bien probada). No veo por qué esto no debería llamarse algo así como CodePoint . La mayoría de las personas que se dan cuenta de que necesitan un tipo de este tipo probablemente estarían pensando en términos de puntos de código de todos modos, no en términos de runas; o bien en términos de puntos de código y runas como partes separadas de su tarea. ᚱᚢᚾᚪ ᛒᛇᚦ ᛥᛁᛚᛖ ᛒᚱᚣᚳᛖᚢ/rúna béoþ stille bryceu/runas todavía se usan. Solo necesito usar runas una vez al año, y generalmente con pergamino y tinta en lugar de algo digital, pero ciertamente hay personas que también las manejan digitalmente. (Incluso con datos del siglo XX, conozco un caso en el que se usan para archivar datos de la era de la Segunda Guerra Mundial).

Grapheme es aún más complicado, ya que uno a menudo quiere ir octetos → caracteres (muy bien manejados por .NET), luego caracteres → puntos de código, y luego puntos de código → grafemas.

marcando esto como disponible por ahora.

Próximos pasos : lo que buscamos es: una propuesta formal que incluya los comentarios anteriores (el nombre real del tipo y las ventajas de usar esto en lugar de usar solo un Int32).

He actualizado el problema, tanto con la API propuesta como con una implementación inicial:

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

En cuanto a la denominación del tipo, se trata tanto de tener un lugar donde pueda buscar las operaciones válidas en el tipo como de tener capacidades específicas del tipo (consulte la implementación para ver algunos ejemplos).

@migueldeicaza antes de marcarlo como listo para revisión, ¿cuáles son sus pensamientos con respecto a las preocupaciones sobre el nombre real del tipo, cree que quizás CodePoint podría ser mejor en términos de describir qué es el tipo?

Creo que el argumento para usar codepoint como nombre es débil.

Usarlo es una idea terrible, a largo plazo, esto debe reemplazar cada uso individual de "char" en el código existente, si esperamos obtener el soporte adecuado de Unicode.

Ojalá hubiéramos podido usar "char" como lo hace Rust, pero lamentablemente, ya lo usamos y tenemos uno roto.

Ir abrazando este nombre es un buen precedente.

Acepto que code point no es el término correcto para usar aquí. Como mínimo, según el estándar Unicode, no incluye valores superiores a 10FFFF (http://unicode.org/glossary/#code_point).

No me gusta el término rune . Creo que tiene un uso existente en Unicode y en otros lugares que solo causará confusión en general. También creo que tiene muchas posibilidades de entrar en conflicto con los tipos de usuarios existentes (especialmente para cosas como Unity, donde una 'Runa' podría representar un objeto de juego específico).

Sin embargo, me gusta la idea de un tipo que cubra el tipo C++ 11 char32_t , solo que con un nombre diferente.

Hay algo que decir sobre Char32 . Es al grano, es análogo a los nombres de tipo de los tipos integrales. Habla al nivel conceptual del personaje, en lugar del nivel del punto de código. No es el nombre de un guión.

Ya que estamos considerando tener nint ¿qué tal nchar ?

El antecedente estaría en las bases de datos nchar y nvarchar

Donde nchar son carácter nacional / carácter nacional y nvarchar es carácter nacional variable / carácter nacional variable; ¿Cuáles son los tipos de campo en los que puede almacenar Unicode, también algún estándar ISO? No estoy seguro de cuál, ¿tal vez SQL?

¿Cuál es este uso Unicode de la runa? Eso es nuevo para mí.

U+16A0 a U+16F8

Se utiliza para hacer referencia a una página de códigos específica en el estándar Unicode. Se ha mencionado varias veces en este hilo: http://unicode.org/charts/PDF/U16A0.pdf

Ah rúnico, no rúnico.

El nombre de respaldo (System.Rune o System.Char32) no es tan importante como la etiqueta que se proyectará en C#.

En primer lugar: sí, sí, y más de esto, por favor. Me encanta esta idea (honestamente, he tenido una idea similar durante mucho tiempo). De hecho, hemos estado usando una clase de cadena personalizada y una estructura de caracteres en nuestra compatibilidad con Git más tarde en Visual Studio desde hace un tiempo (Git habla en Utf-8 y transcodificar todo es muy lento).

Sobre el tema de los nombres de métodos estáticos, ¿podemos evitar los nombres cortos arbitrarios, por favor? Dado que Char.IsPunctuation es el método actual, ¿podemos duplicarlo con Rune.IsPunctuation o similar?

Suponiendo (siempre peligroso) que esto se acepte, ¿podemos tener un rune o c32 intrínseco, o simplemente reemplazar char por completo con la implementación System.Rune ?

Sugiero unichar o uchar aunque uchar parecería que es un carácter sin firmar. Sin embargo, cualquiera que sea el elegido, espero que obtengamos un alias específico de idioma para él. Personalmente, soy un gran fanático del uso de alias de idioma para tipos primitivos.

También estoy de acuerdo con @whoisj : definitivamente preferiría nombres completos de métodos en lugar de abreviaturas cortas.

También estoy de acuerdo con @whoisj : definitivamente preferiría nombres completos de métodos en lugar de abreviaturas cortas.

En mi opinión, un idioma (y sus bibliotecas) debe elegir nombres completos y abreviados, o abusar de las abreviaturas (como C con strcmp, memcpy, etc.)

o simplemente reemplazar char completamente con la implementación System.Rune ?

Eso sería un cambio radical por razones bastante obvias.

Eso sería un cambio radical por razones bastante obvias.

Mis comentarios fueron en su mayoría irónicos y esperanzadores. Un tipo de carácter de 16 bits fue un error desde el principio.

Buena captura en el nombramiento, se arreglará.

Hay otras pequeñas inconsistencias en la API proporcionada, también se intentará solucionarlas.

@migueldeicaza

Ah rúnico, no rúnico.

Runic es el adjetivo, rune el sustantivo. Todos los caracteres rúnicos son runas.

_Rúnica_ es el adjetivo, _rune_ el sustantivo. Todos los caracteres rúnicos son runas.

Tan justo como parece, "Cortana: define _'rune'_" presenta:

una letra de un antiguo alfabeto germánico, relacionado con el alfabeto romano.

Ah, sí, cada vez que veo la palabra "runa", inmediatamente pienso en este oscuro capítulo sobre una especificación que nadie ha leído y que habla sobre "El bloque rúnico Unicode".

😆 Pienso en recuerdos de infancia leyendo a Tolkien.

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

Sí, no pienso específicamente en la especificación, pero sí pienso en el tipo de caracteres a los que se refiere la especificación.

Dices rune y yo pienso en magia, fantasía, acertijos crípticos, lenguas antiguas, etc.

Me alegro de que no veas la palabra "runa" e inmediatamente pienses "Ah, esto claramente se refiere al bloque rúnico Unicode 7.0 cuyo valor se limitará a esos valores únicos en el rango 16A0..16F8".

Sé que Tanner es una sola voz aquí, y algunos de ustedes todavía están pensando "Pero Miguel, veo la palabra 'runa' e inmediatamente pienso en un tipo de datos que solo podría contener 88 valores posibles". Si esto es un problema, estás luchando con él, mi hermano/hermana, tengo noticias para ti: tienes peces más grandes para freír.

He estado siguiendo este hilo durante un tiempo con una mezcla de entusiasmo y vacilación durante poco más de un mes. Asistí a la Conferencia de Internacionalización y Unicode el mes pasado, y ninguna de las presentaciones trató sobre .NET. Hay un problema de percepción con .NET Framework; uno que no es necesariamente inmerecido dada la historia de sus características de globalización. Dicho esto, me encanta programar en C# y absolutamente quiero ver nuevas características que refuercen el lugar de .NET en una comunidad verdaderamente global. Creo que esta propuesta es un buen paso en esa dirección de adoptar los estándares que la comunidad de internacionalización espera del software.

Mi vacilación se ha debido principalmente a las disputas sobre el nombre del tipo. Si bien es cierto que los diseñadores de Go eligieron el nombre "runa", eso es problemático por la razón mencionada anteriormente repetidamente: hay puntos de código que se llaman correctamente runas. Es difícil para mí estar de acuerdo con una propuesta que intenta ceñirse a un estándar respetado y luego redefine la terminología que es parte de la especificación. Además, el argumento de que la mayoría de los desarrolladores ignoran el término es engañoso dado que es más probable que los desarrolladores más interesados ​​en usar este tipo correctamente entiendan la especificación Unicode y tengan una buena idea de qué es realmente una "runa". Imagina la rareza que podría existir si mezclases la terminología:

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

Por supuesto, he tomado el camino fácil aquí, criticando sin proporcionar un nuevo nombre. Creo que la sugerencia anterior de CodePoint es la opción más autodescriptiva (y aparece en la descripción del problema original), pero char32 tendría más paridad con los tipos primitivos existentes (aunque me gustaría dude en decir que no todos los puntos de código son caracteres). Si el objetivo es crear una mejor compatibilidad con Unicode en .NET, apoyo absolutamente ese camino, pero la mejor manera de hacerlo es seguir las especificaciones.

Tres sugerencias:

  1. A la clase Rune le falta el "IsCombining" crítico. Sin eso, no podemos convertir una serie de runas (puntos de código) en una serie de grafemas.
  1. Me encantaría tener también una clase Grapheme correspondiente. Un grafema en este contexto es realmente solo una lista de una o más Runas (Puntos de Código) tales que la primera runa no se combina y el resto de las runas se combinan. El caso de uso es para cuando un desarrollador necesita lidiar con fragmentos de "caracteres visibles". Por ejemplo, a + GRAVE son dos runas que forman un grafema.

  2. En las redes, a menudo obtenemos un trozo de bytes que necesitamos convertir en un objeto similar a una "cadena" donde los bytes pueden no estar completos (por ejemplo, nos informan de algunos bytes, pero el último byte en una secuencia de varios bytes no lo ha hecho). t bastante llegó todavía). No veo ninguna forma obvia de convertir un flujo de bytes en un flujo de runas, de modo que perder el último byte de una secuencia de varios bytes se considere una situación normal que se rectificará cuando obtengamos el siguiente conjunto de bytes.

Y, por último, utilice nombres Unicode y llámelo CodePoint. Sí, el consorcio Unicode hace un trabajo terrible al explicar la diferencia. Pero la solución es agregar documentación clara y utilizable; cualquier otra cosa confunde el problema en lugar de ayudar a aclararlo.

No sé por dónde comenzar con la solicitud de combinación, ni Go, Rust o Swift muestran una API de este tipo en rune, Character o Unicode Scalar (sus nombres para System.Rune ). Proporcione una implementación propuesta.

En los clústeres de grafemas, es una buena idea, debe rastrearse independientemente de System.Rune . Por lo que vale, Swift usa Character para esto, pero Swift tampoco es un gran modelo para manejar cadenas.

Convertir flujos de bytes en una runa adecuada es un problema que pertenece a una API de nivel superior. Dicho esto, puede ver mi implementación ustring que usa el mismo sustrato que mi implementación de System.Rune para ver cómo estos búfer se asignan a cadenas utf8:

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

Documentación, que aún no he actualizado desde que introduje System.Rune en la API, pero la cubre:

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

En cuanto a los nombres, claramente Rust es el mejor con char , pero lo estropeamos. El segundo mejor es ir con rune . Cualquier cosa de más de cuatro caracteres será una molestia para que las personas hagan lo correcto.

Lo siento; Creo que CodePoint es un nombre extraordinariamente bueno. Se explica por sí mismo, es fácil de recordar y se autocompleta con c p .

IsCombining definitivamente sería necesario, pero también lo es conocer la clase de combinación y una vez que tengamos ese IsCombining es en gran parte azúcar, ya que es solo IsCombining => CombiningClass != 0 o IsCombining => CombiningClass != CombiningClass.None . De hecho, los clústeres de grafemas estarían fuera de él nuevamente, pero el punto de partida sería conocer la clase de combinación para el agrupamiento predeterminado, el reordenamiento, etc.

CodePoint es un gran nombre para un tipo sobre puntos de código, y cuatro caracteres no es un límite con el que tengamos que lidiar con otros tipos muy utilizados; string es un 50% más grande y no nos impide usarlo regularmente. Cuatro letras escogidas al azar serían un mejor nombre que repetir el error de Go.

Dado que uint no cumple con CLS, no hay un ctor compatible con CLS que cubra los planos astrales. int también sería necesario.

Las conversiones implícitas bidireccionales pueden provocar que sucedan cosas malas con las sobrecargas, por lo que una dirección quizás debería ser explícita. No está claro cuál. Por un lado uint / int es más amplio que los puntos de código, ya que los valores por debajo de 0 o por encima de 10FFFF 16 no son significativos, y tener esa conversión implícita permite un uso más rápido de más API existentes para números. Por otro lado, puedo ver querer convertir de un número a un punto de código con más frecuencia que al revés.

Dado que uint no cumple con CLS, no hay un ctor compatible con CLS que cubra los planos astrales. int sería necesario también.

Eso es a menos que se introdujera un nuevo tipo intrínseco en el lenguaje común.

JonHanna: ¿quieres decir que estos tres constructores:
operador implícito estático público uint (Runa runa);
Operador implícito estático público Rune (char ch);
Operador implícito estático público Runa (valor uint);

debe ser "int" en lugar de "uint". AFAICT, int cubre fácilmente todo el conjunto de planos astrales (no BMP).

@PeterSmithRedmond Quiero decir que, además de los dos constructores, uno que toma char y otro que toma uint , debería haber uno que tome int , pero sí, también debería haber un int Operador de conversión implicit y qué explicit es otra cuestión). No hay nada de malo en tener uint también para aquellos lenguajes que pueden usarlo; después de todo, es una combinación bastante natural.

Si esto debería reemplazar a System.Char, debería ser posible hacer "aritmética" en él (es decir, ==, !=, >, <no estoy seguro de +, -, *, /) y, lo que es más importante, debería ser compatible con los literales de este escriba, por ejemplo, debería poder escribir:

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


image

Si no es rune , ¿solo otro sinónimo de character que podría funcionar es quizás letter ?

sustantivo

  1. una comunicación escrita o impresa dirigida a una persona u organización y generalmente transmitida por correo.
  2. un símbolo o carácter que se usa convencionalmente en escritura e impresión para representar un sonido del habla y que es parte de un alfabeto.
  3. una pieza de tipo de imprenta que lleva tal símbolo o carácter.

Aunque eso entraría en conflicto con letra vs número

Letra tiene un significado aún más preciso en Unicode (y Net en general) que runa.

Creo que si vamos a hacer de este un tipo de carácter Unicode, debemos seguir las convenciones de nomenclatura de Unicode; que significa _"punto de código"_.

Punto de código . (1) Cualquier valor en el espacio de códigos Unicode; es decir, el rango de números enteros de 0 a 10FFFF16. (Consulte la definición D10 en la Sección 3.4, Caracteres y codificación ). No todos los puntos de código se asignan a caracteres codificados. Ver tipo de punto de código . (2) Un valor, o posición, para un carácter, en cualquier conjunto de caracteres codificados.

O tal vez simplemente nos damos por vencidos y llamamos a un pato "pato" y nos referimos a ellos como Caracteres Unicode (también conocidos como uchar ).

¿Por qué no simplemente resolver esto para usar System.CodePoint en su lugar?
En mi humilde opinión, es más apropiado en términos de terminología de Unicode, y otras personas en el mundo de Java lo están usando. Entonces, en lugar de tener un término por nuestra cuenta, respetemos los términos de Unicode. Tiene más sentido y es más universal en términos de caracteres generales e implementación de cadenas en .NET, sabiendo también el hecho de que String en .NET es una colección de char, y esta colección de char está basada en Unicode.

Lo sé, porque he vivido tanto en el mundo de Java como en el de .NET.
Y tal vez empecemos a tener un borrador de implementación sobre esto.

Realmente hay dos componentes de esto y ambos serían necesarios (CodeUnit en https://github.com/dotnet/corefxlab/issues/1799 por @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 son importantes para representar la codificación de ancho variable y para usar en Span<ubyte> para garantizar que las API de texto estén disponibles en tipos de texto pero no en bytes sin formato.

CodePoint / uchar es importante para un procesamiento sensato; por ejemplo .IndexOf(❤) como ubyte por sí solo no se puede usar para buscar un carácter Unicode multibyte; y enumerar más ubyte sería peligroso, por lo que el enumerador debería trabajar en unidades uchar .

Combinando las dos propuestas quedaría algo así como

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

He estado usando UnicodeScalar en mis implementaciones de prototipo para referirme a un valor escalar Unicode (valores en el rango U+0000..U+10FFFF, inclusive; excluyendo puntos de código sustitutos) y Utf8Char para referirse a la unidad de código UTF-8. Parece que mucha gente prefiere _Rune_ en lugar de _UnicodeScalar_ porque es menos complicado. No me importa demasiado, pero señalaré que el término "valor escalar Unicode" es el mismo término utilizado por la especificación Unicode . ;)

.NET Framework también tiene el concepto de "elemento de texto", que es uno o más escalares que, cuando se combinan, crean un solo grafema indivisible. Más información sobre esto en MSDN . En particular, cuando enumera una cadena, es posible que desee enumerar por unidad de código ( Utf8Char o Char ), valor escalar ( UnicodeScalar ) o elemento de texto, según su escenario particular. Idealmente admitiríamos los tres tipos tanto en String como en Utf8String.

La superficie de la API para nuestro prototipo no está terminada y está sujeta a cambios rápidos, pero puede ver algunos pensamientos actuales en https://github.com/dotnet/corefxlab/tree/utf8string/src/System.Text.Utf8/System /Text y https://github.com/dotnet/corefxlab/blob/master/src/System.Text.Primitives/System/Text/Encoders/Utf8Utility.cs.

Un poco fuera de tema:
¿Debería ser el "elemento de texto" la segmentación definida por "Límites de clúster de grafema" en UAX dotnet/corefx#29 ?

using System;
using System.Globalization;

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

Resultado Esperado:
👩🏻‍👦🏼
👨🏽‍👦🏾‍👦🏿
👩🏼‍👨🏽‍👦🏼‍👧🏽
👩🏻‍👩🏿‍👧🏼‍👧🏾

resultado actual:
👩
🏻

👦
🏼
👨
🏽

👦
🏾

👦
🏿
👩
🏼

👨
🏽

👦
🏼

👧
🏽
👩
🏻

👩
🏿

👧
🏼

👧
🏾

UnicodeScalar sigue siendo muy fácil de escribir. u s c Espacio (autocompleta) Dado que ese es el término correcto y más autodescriptivo, realmente espero que lo entendamos.

@ufcpp Ese es un buen punto. Siéntete libre de abrir un nuevo número para eso. Si no podemos cambiar el comportamiento por razones de compatibilidad, sugiero que desaprobemos ese tipo y creemos un enumerador de grafemas que cumpla con las especificaciones.

ubyte / uchar son confusos. Se leen como unsigned char / unsigned byte dada la convención establecida con ushort / uint / ulong . ¿Quizás char8 / u8char y char32 / u32char son más claros?

En cualquier caso, creo que no estamos alineados sobre si las unidades de código UTF-8 y los puntos de código son:

  1. tipos de datos primitivos de bajo nivel en .NET, como byte , int
  2. un formato de datos para convertir a/desde primitivos existentes, como DateTime , Guid

Y luego, ¿cómo exponemos las API relacionadas con puntos de código dada esa decisión?

La opción 1 significa manejar texto a través de las primitivas char8, char16 y char32 (y las adjuntas u8string, u16string y u32string) como C++17. Entonces char32 como rune es un mal nombre, dado que ya tenemos char16 como char y también necesitamos un tercer nombre para char8 .

La opción 2 significa que byte e int/uint son 'lo suficientemente buenos' para almacenar unidades de código UTF y puntos de código. Esto implica que todas las cadenas siguen siendo UTF-16. CodePoint / rune resuelve problemas de semántica Code Point en lugar de representación binaria, y no está diseñado para IO .

IMO UTF-8/UTF-32 son solo formatos de datos (opción 2). Trátelos como datos (byte/int). CodePoint es más como DateTime o Guid (otro identificador*) que int para mí, no es un tipo primitivo de bajo nivel, no se admite directamente en IO (es decir, BinaryWriter), sin necesidad de intrínsecos.

@miyu El prototipo que presentamos en corefxlab está más cerca de la Opción 1. Hay tipos de datos específicos para representar unidades de código, y estos tipos de datos son para la representación interna de datos textuales y no se pueden usar para transmitir datos textuales a través del cable. (Como usted señala, .NET ya funciona así hoy: System.Char es la unidad de código de una cadena UTF-16, pero System.Char no se puede enviar por cable).

Además, hay API para convertir entre byte[] / Span<byte> / etc. (esta es la representación binaria de todos los datos y es apropiada para E/S) y tipos primitivos como Utf8String / String / Guid / etc. Algunos de estos son más sencillos que otros. Por ejemplo, podemos exponer una propiedad conveniente Utf8String.Bytes que devuelve ReadOnlySpan<byte> para usar en E/S, y este captador de propiedades puede tener una complejidad O(1). No introduciríamos tal propiedad en el tipo String , aunque podría imaginarse tener un método de conveniencia String.ToUtf8Bytes() . Y aunque existiría una propiedad Utf8String.Bytes , el tipo elemental de enumeración sobre una instancia Utf8String directamente no sería byte . Sería Utf8CodeUnit (nombre TBD) o UnicodeScalar , lo que creamos que tenga más sentido para los tipos de aplicaciones que los desarrolladores quieren crear.

Una idea tonta: ¿qué tal wchar (_carácter ancho_)? Actualmente, la mayoría de los entornos de compilación de C y C++ (fuera de Windows) ya usan wchar_t para representar el equivalente funcional de una unidad de código de 32 bits. Windows es una excepción notable, donde wchar_t se define como un tipo de 16 bits, pero los desarrolladores que p/invocan en Windows hoy en día ya deben ser conscientes de las diferencias de ancho de bit entre un .NET char y un estilo C char .

El tipo/palabra clave wchar violaría nuestras convenciones de nomenclatura, pero lo descartamos para su consideración.

Una idea tonta: ¿qué tal wchar (carácter ancho)?

Funciona para mi

El tipo / palabra clave wchar violaría nuestras convenciones de nomenclatura, ...

No parece que vayamos a obtener una palabra clave corta del lenguaje C#

https://github.com/dotnet/apireviews/pull/64#discussion_r196962756 parece extremadamente poco probable que introduzcamos palabras clave de idioma para estos tipos, ya que tendrían que ser contextuales (es decir, dependiendo de si pueden resolverse en un tipo con el nombre de la palabra clave que aún tendrían que vincular a ese tipo, en lugar del tipo representado por la palabra clave).

Entonces, si queremos algo bueno... es decir NotLotsOfCapitalFullWords ...

Si bien normalmente me gustan las convenciones de nomenclatura de .NET, un nombre largo es un poco ofensivo para esencialmente un int que probablemente también se usará en genéricos y como variables de bucle.

por ejemplo, nadie lo hace

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

¿Ellos? (Seguramente...)

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

es mucho peor

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

Parece bien...

rune , wchar y uchar (sugerido en otro hilo) me suenan bien. ¿Alguna sugerencia para un par de string ? wstring , ustring , u otro?

... y ¿por qué no obtener una palabra clave del lenguaje C#? Claro, no tener uno para el primer lanzamiento tiene sentido, pero si esto va en el futuro al manejo de cadenas, no tener una palabra clave no solo es falso, sino abiertamente hostil hacia su adopción.

/CC @MadsTorgersen @jaredpar

¿Por qué no obtener una palabra clave del lenguaje C#?

Las palabras clave nuevas están rompiendo cambios el 100% del tiempo. No importa qué palabra elija, hay una empresa que tiene un tipo de ese nombre que se usa en todas partes en su proyecto. La única opción que tenemos son las palabras clave contextuales: var por ejemplo.

Tengo sentimientos encontrados sobre el uso de una palabra clave contextual para esto. Las palabras clave de tipo existentes ( int , string , etc...) tienen una ventaja concreta sobre el nombre de tipo real ( Int32 , String ):

  • string : se refiere al tipo System.String en el ensamblado que el compilador identifica como corelib. Este nombre tiene cero ambigüedad asociada con él.
  • String : el compilador no tiene conocimiento de este tipo. Es solo un tipo como cualquier otro y pasa por todas las mismas reglas de búsqueda que los tipos que defina. Puede ser equivalente a string o puede no serlo.

Una vez que introduzcamos aquí las palabras clave contextuales, entonces rune podría ser:

  • El tipo System.Rune dentro del ensamblado corelib
  • El tipo rune que definiste hace dos años cuando leíste sobre Go .

La búsqueda de rune es tan ambigua como String por lo que no veo una gran ventaja en tenerla como palabra clave contextual.

Por cierto: esta es la razón por la que deberías usar string y no String 😄

Por cierto: esta es la razón por la que deberías usar string y no String

El 99% de la razón por la que creo que la gente quiere una palabra clave de idioma. El otro 1% es simplemente "se ve mejor" 😏

Pulgares hacia abajo por una fuerte aversión a la palabra clave "runa".

Una palabra mejor es glifo, ya que representa el concepto general de un símbolo elemental en tipografía.

Rune es un tipo específico de glifo que está irónicamente definido por Unicode. Hacer referencia a Go como estado de la técnica es algo ridículo. El arte anterior para las runas es lo que se escribió en el año 150 dC y las piedras rúnicas físicas reales. No es lo que alguien en Redmond cree que es una runa. Tratar de redefinir conceptos existentes como este es inusual ya que .NET generalmente tiene una superficie de API bien diseñada. Esta es una rara excepción de nombres de API muy pobres y quiero expresar mi descontento.

Una palabra mejor es glifo, ya que representa el concepto general de un símbolo elemental en tipografía.

El problema es que "Glifo" es un término usado cuando se representa el código Unicode en texto visible (de: utf8everywhere.org )

glifo

Una forma particular dentro de una fuente. Las fuentes son colecciones de glifos diseñados por un diseñador tipográfico. Es responsabilidad del motor de representación y modelado de texto convertir una secuencia de puntos de código en una secuencia de glifos dentro de la fuente especificada. Las reglas para esta conversión pueden ser complicadas, dependen de la configuración regional y están más allá del alcance del estándar Unicode.

Hacer referencia a Go como estado de la técnica es algo ridículo.

Usando el término que Rob Pike y Ken Thompson usaron al crear Utf-8 https://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt

Rob Pike trabaja en Go ahora, razón por la cual usa el término original.

Rune es un tipo específico de glifo que está irónicamente definido por Unicode.

Runic está definido por Unicode, Rune no lo es

Runic está definido por Unicode, Rune no lo es

No creo que esta sea una declaración precisa, la última especificación de Unicode (http://www.unicode.org/versions/Unicode11.0.0/UnicodeStandard-11.0.pdf) tiene 37 resultados para "rune" (solo 36 son válidos , el último es parte de una palabra más grande) y siempre se usa para referirse a letras individuales del Alfabeto Rúnico.

No creo que esta sea una declaración precisa, la última especificación Unicode tiene 37 resultados para "rune"

En el cuerpo del texto que describe las motivaciones; no en ningún nombre de personaje o nombre de bloque de texto (donde su carácter rúnico y rúnico)

En el cuerpo del texto que describe las motivaciones; no en ningún nombre de personaje o nombre de bloque de texto (donde su carácter rúnico y rúnico)

Bien, justo. Pero luego volvemos al problema de que la especificación actual de Unicode no define el término "Runa" y cuando se usa, es para texto informativo que describe "caracteres rúnicos".

Lo que define y usa formalmente para describir cosas es "Punto de código" y "Unidad de código".

  • Incluso si, históricamente, los creadores originales usaron el término "Runa", la especificación oficial no lo hace (y me imagino que tenían buenas razones para no usarlo).

Tiene que ser corto o su uso se pone feo

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

contra

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

Para la longitud, optaría totalmente por CodePoint.IsWhiteSpace y str.GetCodePointAt , pero Rune también es divertido y no me importa.

@jnm2 No usaríamos GetCodePointAt cuando se trata de cadenas. Es demasiado ambiguo: no sabemos si quería el char que estaba en ese índice (ya que todos los char , incluso los sustitutos no emparejados, también son puntos de código válidos) o el escalar / runa que estaba en ese índice.

@GrabYourPitchforks ¿Puede GetRuneAt evitar el mismo problema, o está diciendo que ninguno de los dos tendría sentido?

@ jnm2 Solo decía que CodePoint en particular es demasiado ambiguo en este escenario. De lo contrario, el nombre del método GetXyzAt debería coincidir con el nombre del tipo Xyz que finalmente se incluye.

FYI, la implementación principal ahora está registrada (ver https://github.com/dotnet/coreclr/pull/20935). Déle algo de tiempo para que se propague a corefx, luego las API de referencia llegarán a través de https://github.com/dotnet/corefx/pull/33395. Siéntase libre de dejar este problema abierto o resolverlo como mejor le parezca.

No espero influenciar a nadie ni poder cambiar nada, solo para que conste:

Una palabra mejor es glifo, ya que representa el concepto general de un símbolo elemental en tipografía.

El problema es que "Glifo" es un término usado cuando se representa el código Unicode en texto visible (de: utf8everywhere.org )

Esa línea de razonamiento tampoco es compatible con runa, porque "runa" ha sido un término utilizado durante más de mil años a lo largo de la historia, mucho antes de que existieran Unicode, transistores, Microsoft o código abierto. Al menos indica que algunos aplican arbitrariamente diferentes estándares a diferentes propuestas, lo que obviamente no es consistente, por lo que tal vez se trate más de quién fue el primero o quién es el más fuerte en lugar del argumento más coherente, qué sé yo. Solo llego tarde tratando de entender el proceso, pero no tiene sentido.

Hacer referencia a Go como estado de la técnica es algo ridículo.

Usando el término que Rob Pike y Ken Thompson usaron al crear Utf-8 https://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt

Rob Pike trabaja en Go ahora, razón por la cual usa el término original.

Go y Rob Pike son relativamente nuevos en este tema. En realidad, su opinión es algo irrelevante en términos de definir qué es una runa históricamente y en la literatura y sociedad popular. Rob no martilló ninguna piedra rúnica a mano, por lo que tiene pocas calificaciones para definir qué es una runa. Apuesto a que ni siquiera puede escribir o leer escritura de runas, pero eso es mi suposición. En el mejor de los casos, puede capturar ese concepto a través de la codificación, pero no puede entrar y decir que un carácter chino, escritura árabe o Hangul o una cara sonriente es una runa o cualquier otra cosa que sea un "Punto de código" ahora también es una Runa, o algo así. Casi parece pisotear irrespetuosamente el término, mira, ahora todo puede ser una runa, lo que significa que las runas no son más que un término comodín de cuatro letras para referirse a algo esotérico en el dominio de la codificación de texto.

Rune es un tipo específico de glifo que está irónicamente definido por Unicode.

Runic está definido por Unicode, Rune no lo es

No se supone que Unicode redefina lo que es una runa o una runa. Si lo hacen, se están extralimitando en su mandato. No tienen por qué decirle al público qué es una runa. De hecho, no tienen por qué definir ningún nuevo idioma o sistema de caracteres. No pueden simplemente apropiarse de una palabra que ya es un término claramente sobrecargado desde hace mil años y luego corren vitoreando como si hubieran inventado un nuevo concepto. La escritura rúnica consiste solo en runas, y las runas ya son un concepto establecido. Si le preguntas a una persona al azar en la calle qué es una runa, no pensará en Unicode.

Además de todos los problemas anteriores, la runa es una metáfora pobre que es la peor parte. No aclara nada. Simplemente agrega otro nivel de confusión. Cualquier recién llegado al tema ahora necesita pasar por una ronda de explicación y lectura de desambiguación porque todos vienen con el contexto de que una runa es un sistema de escritura histórico utilizado en ciertas culturas. La explicación tendrá que ser algo como esto: "Una runa es un punto de código Unicode". "¿Pero por qué no llamarlo punto de código?" "Bueno, porque es demasiado largo", o "Alguien decidió que le gusta la runa". Básicamente, porque alguien piensa que 9 letras es demasiado en comparación con 4 (aunque tienen autocompletado con Intellisense y no es nada en comparación con Java Kingdom Of Nouns), ahora tenemos que lidiar con esta confusión y explicárselo a miles. de desarrolladores que pueden necesitar incursionar en Unicode. Simplemente use una declaración de uso para acortar el término si lo usa mucho en el código.

Tampoco tiene que ser UnicodeCodePoint, simplemente puede ser CodePoint. Esto ya es único. Hay muchos términos de API que son más largos que "CodePoint", por lo que debería ser suficiente. Si aún es demasiado largo, simplemente use una declaración de uso con alguna abreviatura.

Preveo que esto se convertirá en una de esas preguntas de entrevista que realmente no agregan mucho valor ni tienen una base lógica en nada útil. Al menos para la metáfora "hito", mientras estamos en el tema de las palabras simbólicas utilizadas en el desarrollo de software basado en conceptos derivados de piedra y roca, un hito tiene un significado descriptivo real. Inmediatamente comunica un concepto con el que todos están familiarizados. Ajá, un hito, como cuando vas en un viaje largo y pasas por el sendero. Es una bonita metáfora del mundo real que en realidad ayuda a visualizar algo y puede convertirse en un lenguaje de gestión al instante. No puedo imaginar a la gente hablando de runas de esta manera a menos que estén íntimamente familiarizados con el tema, en cuyo momento ya sabrán que es solo un término engañoso para el punto de código.

Una palabra mejor es glifo, ya que representa el concepto general de un símbolo elemental en tipografía.

El problema es que "Glifo" es un término que se usa cuando se convierte el Unicode en texto visible (de: utf8everywhere.org)

Esa línea de razonamiento tampoco es compatible con runa, porque "runa" ha sido un término utilizado durante más de mil años a lo largo de la historia, mucho antes de que existieran Unicode, transistores, Microsoft o código abierto.

Mi punto era que la palabra "glifo" es problemática ya que ya se usa como uno de los conceptos en la representación del texto; es la representación gráfica de ese carácter en una fuente particular. Entonces, un personaje puede ser representado por muchos glifos diferentes.

... otra vez con @benaadams teniendo la vista de 10,000 metros de las cosas y la respuesta correcta 😁

Honestamente, vamos a tener que vivir con el viejo adagio: "puedes hacer feliz a algunas personas todo el tiempo, y a todas las personas felices algunas veces; pero no puedes hacer felices a todas las personas todo el tiempo". el tiempo." Esta es en gran medida una situación de la primera.

sigilo?

Exit, pursued by a bear.

Como alguien que usaría esta API extensamente, estoy votando fuertemente por el punto de código. La terminología de Unicode ya es lo suficientemente confusa y las inconsistencias ya abundan. Hará mi vida mucho más fácil si puedo decir "punto de código" en todas partes.

Estoy acostado en la cama ahora mismo. Si me doy la vuelta, me encuentro frente a una pizarra apoyada contra mi pared. Durante meses, esa pizarra ha sido el hogar de varios garabatos y gráficos mientras trato de descubrir cómo manejar los IDN de manera eficiente en C#. Lo trato como una reliquia que he convocado desde las profundidades del infierno. Si tratara de explicar la lógica que describe, no sería capaz de hacerlo.

Por favor, no me hagas la vida más difícil. Un punto de código es un punto de código. No es una runa, un glifo, un carácter, un grafema o incluso un símbolo. No es necesario que represente nada significativo para un ser humano; podría ser un código de control. Puede que no represente un símbolo visual, como implica el nombre "runa". Es solo un punto de código.

Un argumento más concreto es que “runa” implica la representación de un solo grafema, lo que muy a menudo no es el caso. Si cuento el número de puntos de código y el número de grafemas, podría obtener dos números muy diferentes. La misma secuencia de grafemas podría representarse mediante dos series distintas de puntos de código.

Una palabra mejor es glifo, ya que representa el concepto general de un símbolo elemental en tipografía.

Eso es aun peor. Un solo punto de código podría estar representado por múltiples glifos, y un solo glifo podría representar múltiples puntos de código. El mapeo exacto puede variar según el sistema, el programa, el tipo de letra...

Todas estas palabras tienen significados técnicos muy específicos. Si bien las diferencias pueden parecer insignificantes en el contexto de esta propuesta, tienen consecuencias reales en otros lugares, especialmente en idiomas distintos del inglés.

Solo como un ejemplo de lo difícil que puede ser manejar un texto, incluso en un idioma tan común como el alemán:

  1. Convierte ß a mayúsculas y obtendrás SS .
  2. Conviértalo de nuevo a minúsculas y obtendrá ss .

Problemas:

  • ¿Qué debería devolver char.ToUpper('ß') ? (Tiene que devolver un solo carácter).
  • Se agregó una versión mayúscula de ß que mi teléfono no puede ingresar en este cuadro de texto a Unicode 5.1. Si trato de pegarlo, obtengo SS. Ahora las conversiones superior/inferior son aún más ambiguas.
  • Cambiar la carcasa de una cuerda cambia su longitud.
  • Los cambios de caso no son idempotentes ni reversibles.
  • No puede realizar una comparación que no distinga entre mayúsculas y minúsculas simplemente escribiendo en minúsculas cada cadena.

Aunque este no es un ejemplo directo de una situación en la que la terminología causa problemas, demuestra cómo hay casos extremos en los que normalmente no pensamos. Dar a cada término un significado distinto y coherente ayuda a los programadores a comunicar estos problemas. Si le pido a un compañero de equipo que escriba una función para contar grafemas, sabe exactamente lo que va a contar y cómo hacerlo. Si les pido que cuenten los puntos de código, nuevamente, saben exactamente qué hacer. Estas definiciones son independientes de los lenguajes y tecnologías que estemos usando.

Si le pido a un desarrollador de JavaScript que cuente runas, me mirarán como si tuviera tres cabezas.

Wikipedia dice

Unicode define un espacio de código de 1 114 112 puntos de código en el rango de 0hex a 10FFFFhex

Punto de código parece ser el nombre oficial. He leído este hilo y no he encontrado un argumento forzado de por qué el punto de código sería incorrecto.

Acepto que punto de código no es el término correcto para usar aquí. Como mínimo, según el estándar Unicode, no incluye valores superiores a 10FFFF (http://unicode.org/glossary/#code_point).

¿Quizás esa oración está mal? Dice "cualquier valor en el espacio del código". Por lo tanto, claramente significa todo y, al mismo tiempo, se equivoca en el número entero.

Además, "runa" tiene un significado en el mundo real que no tiene nada que ver con Unicode. En Alemania, la palabra "Runa" tiene connotaciones nazis porque las runas tienen una historia "germánica" a la que a los nazis les gustaba referirse.

Encuentro que "runa" es un nombre confuso. ¿A alguien aquí realmente le gusta "rune" o los argumentos para ello se basan en la corrección? Intuitivamente, es un nombre realmente malo.

¿Quizás esa oración está mal? Dice "cualquier valor en el espacio del código". Por lo tanto, claramente significa todo y, al mismo tiempo, se equivoca en el número entero.

Esa oración es correcta. El espacio de código es de U+0000 a U+10FFFF. En teoría, Unicode podría expandirse más allá de eso algún día, pero rompería UTF-8 y UTF-16. Necesitaríamos nuevas codificaciones.

Editar: en realidad, no me cites en la rotura de UTF-16, pero estoy bastante seguro de que rompería UTF-8. UTF-8 definitivamente no puede representar 0xFFFFFF (2^24 -1).

Edición 2: para aclarar, Unicode establece que los puntos de código nunca pueden exceder U + 10FFFF. Eso no significa que actualmente haya 0x110000 puntos de código; la mayoría de esos puntos de código no están asignados.

@Zenexer @GSPP

Este tipo, tal como está registrado actualmente en el maestro ( System.Text.Rune ), se asigna muy específicamente a un "valor escalar Unicode" ( consulte el glosario ). Los factores del tipo arrojarán una excepción si intenta construirlo a partir de los valores -1 , 0xD800 o 0x110000 , ya que estos no son valores escalares según la especificación Unicode. Si toma un parámetro Rune como entrada para su método, no tiene que realizar ninguna verificación de validación en él. El sistema de tipos ya ha asegurado que se construyó a partir de un valor escalar válido.

Re: conversión de casos, todas las API de conversión de casos en .NET Framework, a menos que se indique lo contrario, utilizan una técnica llamada plegamiento de casos simple. Según las reglas para el plegamiento simple de mayúsculas y minúsculas, para cualquier valor escalar de entrada, también se garantiza que las formas de salida en minúsculas, mayúsculas y títulos sean exactamente un valor escalar. (Algunas entradas, como los dígitos 0-9 o los símbolos de puntuación, no tienen entradas en el mapa de conversión de mayúsculas y minúsculas. En estos casos, las operaciones como _ToUpper_ simplemente devuelven el valor escalar de entrada). en el Plano Básico Multilingüe (BMP), entonces la salida también debe estar en el BMP; y si la entrada está en un plano suplementario, la salida también debe estar en un plano suplementario.

Hay algunas consecuencias para esto. Primero, Rune.ToUpper y sus amigos siempre devolverán un solo valor de _Rune_ (escalar). En segundo lugar, String.ToUpper y sus amigos siempre devolverán una cadena con exactamente la misma longitud que su entrada. Esto significa que una cadena que contiene 'ß' (eszett minúscula), después de una operación de conversión de mayúsculas y minúsculas, puede terminar conteniendo 'ß' (sin cambios) o 'ẞ' (eszett mayúscula), dependiendo de la cultura que se utilice. Pero _no_ contendrá "SS", porque esto cambiaría la longitud de la cadena, y casi todas las API de conversión de casos .NET expuestas públicamente usan reglas simples de plegado de casos. En tercer lugar, Utf8String.ToUpper y amigos (aún no registrados) _no_ garantizan que devolverán un valor cuya propiedad _Length_ coincida con la propiedad _Length_ del valor de entrada. (La cantidad de unidades de código UTF-16 en una cadena no puede cambiar después de un simple plegado de mayúsculas y minúsculas, pero la cantidad de unidades de código UTF-8 en una cadena puede cambiar. Esto se debe a cómo UTF-16 y UTF-16 codifican los valores BMP). 8.)

Hay algunas API de .NET que utilizan internamente reglas de plegado de casos complejas en lugar de reglas de plegado de casos simples. String.Equals , String.IndexOf , String.Contains , y operaciones similares usan reglas complejas de plegado de casos debajo de las cubiertas, según la cultura. Por lo tanto, si su cultura se establece en _de-DE_, la cadena de un carácter "ß" y la cadena de dos caracteres "SS" se compararán como iguales si pasa _CurrentCultureIgnoreCase_.

@GrabYourPitchforks Me opongo principalmente a la elección del nombre. El ejemplo de casefolding fue simplemente para enfatizar lo complicado que puede ser Unicode (y el texto en general). Siempre que haya alguna forma de manejar la normalización , no me importa demasiado cómo funcionan las operaciones simples, ya que convertiré a NFKD para todo de todos modos para mi caso de uso.

Esa oración es correcta. El espacio de código es de U+0000 a U+10FFFF. En teoría, Unicode podría expandirse más allá de eso algún día, pero rompería UTF-8 y UTF-16. Necesitaríamos nuevas codificaciones.

Solo para ser quisquilloso (o, si la gente está interesada): en teoría, el algoritmo UTF-8 funciona para hasta 42 bits (prefijo byte 0xFF y 7 bytes de carga útil de 6 bits), y originalmente, las primeras especificaciones cubrían los 31 bits completos. espacio de bits de esas versiones antiguas del conjunto de caracteres universales (UCS4); sin embargo, las especificaciones actuales (RFC 3629, estándar Unicode, anexo D de ISO/IEC 10646) acuerdan restringirlo al rango actual de puntos de código válidos (U+ 0000 a U+10FFFF).

Para UTF-16, la situación es más difícil. Pero podrían reservar puntos de código en un plano superior como "Escapes" para 32 bits o más. Como los planos 3 a 13 no están definidos actualmente, podrían reservar dos de ellos como "plano sustituto bajo" y "plano sustituto alto". Luego, un punto de código de 32 bits se dividiría en dos valores de 16 bits (uno en cada plano), y luego cada valor se codificaría usando dos sustitutos "clásicos", usando efectivamente 4 unidades de código de 16 bits cada una para codificar un punto de código de 32 bits.

Por cierto, AFAICS, el consorcio Unicode ha declarado públicamente que nunca asignarán puntos de código por encima de U+10FFFF, así que en la práctica, espero estar retirado mucho antes de que eso suceda. :guiño:

Este tipo, tal como está registrado actualmente en el maestro ( System.Text.Rune ), se asigna muy específicamente a un "valor escalar Unicode"

@GrabYourPitchforks gracias por esa aclaración. Esto significa que la estructura no representa un punto de código. Así que ese nombre de hecho sería incorrecto.

Supongo que UnicodeScalar es demasiado arcano como nombre...

@GrabYourPitchforks , ¿qué queda por hacer para este problema?

@stephentoub No hay ninguna funcionalidad adicional planeada para el tipo de Rune en la caja para 3.0, pero @migueldeicaza tenía ideas para extender el alcance del tipo, incluso para cosas como grupos de grafemas. (Lo más parecido que tenemos en la caja es TextElementEnumerator , que es un tipo muy desactualizado). Algunas de esas ideas se mencionaron en este hilo, pero aún no hay nada concreto.

Podríamos dejar este problema abierto en caso de que la comunidad quiera discutir más los escenarios, o podríamos indicarle a la gente que abra nuevos problemas si quieren hacer sugerencias específicas. TBH No tengo una preferencia fuerte.

Gracias. Dado que Rune ya se presentó y las API descritas aquí (o aproximaciones de las mismas) ya están expuestas, cerremos esto. El soporte adicional se puede abordar a través de problemas separados.

Entonces, ¿esto está esencialmente estabilizado en este punto? Porque, con toda honestidad, este terrible nombre, que no se alinea con ninguna información que encontrará sobre Unicode de fuentes buenas y precisas, y tiene el desafortunado matiz de implicar un glifo en lugar de un carácter no imprimible, solo va a empeorar la ya terrible comprensión de Unicode por parte de su programador promedio.

Sé que esto se ha integrado en este punto, pero solo quiero intervenir en la parte Rune y el desacuerdo de algunas personas sobre el nombre.

Encontré Rune por primera vez en Plan 9 y, como otros, lo han visto en Go y otros. Cuando los msdocs comenzaron a enumerar Rune , sabía exactamente qué era antes de leer.

En al menos dos instancias, Plan 9 y Go, las personas responsables de UTF-8 usan el nombre Rune . Creo que es seguro decir que ya pensaron en estas preocupaciones y aún pensaron que Rune era razonable. Runic ya no es realmente un sistema de escritura usado, aparte de algunos tradicionalistas. Y Rune significa el grafema en ese sistema, al igual que esencialmente significa el grafema aquí (excepto en casos como los caracteres de control.

Realmente veo poco mal con el nombre. Runic es un sistema de escritura tan antiguo que dudo mucho que su programador promedio lo confunda, y ya ha habido un estándar de facto de varias décadas de Rune para los "caracteres" Unicode adecuados.

@Entomía

al igual que esencialmente significa el grafema aquí (excepto en casos como caracteres de control.

Esto simplemente no es cierto. Unicode contiene una gran cantidad de puntos de código precompuestos que representan múltiples grafemas (generalmente combinaciones de letras y signos diacríticos), y estos se usan comúnmente para escribir idiomas como el francés y el español, y casi todo el texto computarizado en estos idiomas usará esos códigos. puntos.

Por el contrario, incluso cuando un solo punto de código representa un grafema, es muy común que se combinen en un _grupo de grafemas_, lo cual es esencial para el manejo adecuado del texto en la mayoría de los idiomas indios. Por lo tanto, un solo carácter percibido por el usuario cuando se mueve con las teclas de flecha a menudo corresponde a múltiples puntos de código en secuencia. Por lo tanto, no puede haber una correspondencia fácil entre los puntos de código y los grafemas o los grupos de grafemas. Incluso "personaje" probablemente sería un mejor nombre, considerando que los programadores están acostumbrados a considerar a los personajes extraños y extravagantes en este punto, mientras que "runa" da la impresión de que el programador ha resuelto el problema de averiguar los límites de carácter percibidos por el usuario. ya cuando en realidad no lo ha sido.

Cuando los msdocs comenzaron a enumerar Rune, sabía exactamente qué era antes de leer.

El hecho de que pensara que el nombre runa describía bien los grafemas es una muy buena evidencia del problema que tengo aquí: el nombre "runa" les da a los programadores una falsa sensación de seguridad al hacer que sea más fácil asumir que existe tal correspondencia.

En al menos dos instancias, Plan 9 y Go, las personas responsables de UTF-8 usan el nombre Rune .

Si bien tengo mucho respeto por Ken Thompson y Rob Pike, su trabajo aquí fue esencialmente idear un esquema muy inteligente para codificar una serie de números enteros de longitud variable. No son expertos en Unicode como un todo y no estoy de acuerdo con ellos en este tema. Admito que tampoco soy un experto en Unicode, pero no creo que la apelación a la autoridad aquí sea tan fuerte como podría parecer.

y ya ha habido un estándar de facto de Rune de varias décadas para los "caracteres" Unicode adecuados.

¿“Estándar” dices? En su mayoría, solo han sido estos dos empujando el nombre, y algunos lenguajes de programación menores, como Nim, lo adoptaron de Go. Y, por supuesto, debo repetir nuevamente que un punto de código no representa un solo "carácter Unicode adecuado", ya sea en el sentido de selección, movimiento de teclas de flecha, grafemas o grupos de grafemas.

...esencialmente significa el grafema aquí...

Sí, ya que no exactamente, pero aproximadamente lo suficientemente cerca. Los grafemas, al menos como se definen en lingüística, son los componentes ortográficos que componen un sistema de escritura y se utilizan para expresar fonemas. Estos no son una cosa 1: 1. En silabarios y logosilabarios, un solo grafema puede representar múltiples fonemas, típicamente un par de consonante-vocal. Por el contrario, alfabéticamente, los idiomas a menudo tienen casos de múltiples grafemas que representan un solo fonema, como "th" en inglés que es responsable del arcaico eth y thorn, según la palabra específica. Entonces, ni siquiera puede encontrar un acuerdo entre los idiomas en cuanto a si una letra como 'á' es su propia letra única, o 'a' con acento. Ni siquiera podemos establecer la coherencia en los idiomas de miles de años. No vamos a tener una adición perfectamente consistente además de eso, esa es la codificación de estos.

Dado que está defendiendo una semántica extremadamente estricta, lo que UNICODE llama un "grupo de grafemas" a menudo es en lingüística un solo grafema. ¿Esto no es válido para UNICODE? No. ¿Significa esto que UNICODE necesita cambiarle el nombre? ¿No porque? Porque contexto. Los campos tienen su propia jerga, y siempre que no haya confusión dentro de un solo campo, no hay problema.

No veo el nombre como un gran problema. Msdocs tiene claro qué es Rune en el resumen. Si la gente no lee los documentos, es su propio problema. La gente no está reaccionando con vehemencia a 'Stream' y diciendo tonterías como "¡oh, pero qué pasa si la gente piensa que es un río pequeño, porque ya tiene el mismo nombre!" No.

@Serentty @Entomy Ambos también podrían estar interesados ​​en la clase StringInfo , que expone el concepto actual de Unicode "grupos de grafemas extendidos". El tipo StringInfo es bastante antiguo y, como resultado, implementa una versión muy antigua del estándar Unicode, pero hay trabajo activo para actualizarlo para que cumpla con UAX #29, Sec.

Sí, ya que no exactamente, pero aproximadamente lo suficientemente cerca.

Creo que el tema de las representaciones compuestas frente a las descompuestas hace que esto no sea cierto. Si nos guiamos por la definición lingüística de un grafema aquí en lugar de cualquier tipo de definición relacionada con la informática, entonces 한 y 한 son exactamente la misma secuencia de grafemas (tres hangul jamo que representan la sílaba _han_ como los segmentos HAN), y sin embargo, el primero es solo un punto de código, mientras que el segundo es una secuencia de tres.

Los campos tienen su propia jerga, y siempre que no haya confusión dentro de un solo campo, no hay problema.

Este es exactamente mi punto también. Unicode es un sistema realmente complicado con su propia terminología, entonces, ¿por qué tratar de forzar algún tipo de término "intuitivo" a medias cuando no se alinea con tanta precisión? Los puntos de código son puntos de código. No tienen un paralelo lingüístico, y tratar de ser intuitivos con solo un 75 % de precisión es una receta para el mismo tipo de desastre del que C# todavía está tratando de recuperarse.

Dado que está defendiendo una semántica extremadamente estricta, lo que UNICODE llama un "grupo de grafemas" a menudo es en lingüística un solo grafema.

En el estándar, se permite que un grupo comprenda solo un único grafema. No hay nada malo con esto aquí. Un _cluster_ es una unidad de selección de texto y movimiento del cursor.

No veo el nombre como un gran problema. Msdocs tiene claro qué es Rune en el resumen. Si la gente no lee los documentos, es su propio problema.

Este es el argumento de que “los programadores deben ser más inteligentes” que surge repetidamente en defensa de las malas decisiones de diseño. Si los programadores necesitan leer la documentación y aprender que una runa es un punto de código Unicode de todos modos, ¿cuál es el punto de llamarlo un nombre más "intuitivo" en primer lugar? El argumento aquí parece ser que el "punto de código" es confuso, por lo que tiene sentido elegir un nombre más intuitivo, pero luego, cuando se enfrenta al problema de que el nombre es engañoso, la defensa es que los programadores deberían saber qué es un punto de código de todos modos. de leer la documentación. Si ese es el caso, ¿por qué no simplemente llamar al tipo CodePoint y hacer que sea más fácil para los programadores buscar y aprender? Todo esto deja de lado el problema de que la documentación de .NET es bastante terrible con respecto a Unicode en primer lugar, trata los pares sustitutos como una ocurrencia tardía en un mundo de "caracteres Unicode de 16 bits".

Este es el argumento de que “los programadores deben ser más inteligentes” que surge repetidamente en defensa de las malas decisiones de diseño.

Nunca dije esto.

El argumento aquí parece ser que el "punto de código" es confuso

Nunca dije esto tampoco.

La gente no está reaccionando con vehemencia a 'Stream' y diciendo tonterías como "¡oh, pero qué pasa si la gente piensa que es un río pequeño, porque ya tiene el mismo nombre!" No.

Estoy diciendo que los programadores son lo suficientemente inteligentes como para no pensar que Rune es específicamente una runa rúnica, de la misma manera que saben que Stream no es un río pequeño.

Déjame repetir esto

Estoy diciendo que los programadores son lo suficientemente inteligentes como para resolver esto. Estás poniendo palabras en mi boca.

No veo el nombre como un gran problema. Msdocs tiene claro qué es Rune en el resumen. Si la gente no lee los documentos, es su propio problema.

Esto es a lo que me refiero aquí. El argumento a favor del nombre “runa” se basa en la intuición y la conexión intuitiva con la noción de grafema. Usted mismo estaba argumentando que los dos se alinearon lo suficientemente cerca como para que no fuera un problema. Cuando señalé todas las formas en que esa intuición estaba equivocada y la correspondencia podría ser muy mala, su respuesta fue esencialmente que no importaba porque los programadores necesitaban leer la documentación de todos modos. Esto es lo que quiero decir con "los programadores deben ser más inteligentes". La documentación no es una excusa para nombres engañosos cuando no hay una razón heredada para ellos.

Estoy diciendo que los programadores son lo suficientemente inteligentes como para no pensar que Rune es específicamente una runa rúnica, de la misma manera que saben que Stream no es un río pequeño.

Mi argumento aquí no es que la gente lo confunda con runas rúnicas. Mi argumento es que la gente lo confundirá con glifos, grafemas y grupos de grafemas que, a pesar de su insistencia, se correlacionan muy mal con los puntos de código.

Estoy diciendo que los programadores son lo suficientemente inteligentes como para resolver esto. Estás poniendo palabras en mi boca.

Lo suficientemente inteligente como para darse cuenta de que no son runas germánicas reales, claro. ¿Pero darse cuenta de que no son glifos, grafemas o grupos de grafemas? Mi experiencia real con la calidad del manejo de Unicode en la mayoría de los programas dice que no.

Si la gente no lee los documentos, es su propio problema.

Sí, y mantengo esto. No por una cuestión de deficiencia en la inteligencia, sino más bien por una tendencia a las suposiciones apresuradas.

Si un programador asume que String significa un trozo de cuerda fuerte y delgado, hecho de la torsión de hilos, porque, sí, significa que eso no se considera un problema con el nombre String .

Si un programador asume que Char significa un material carbonizado como carbón o un tipo particular de trucha, eso no se considera un problema con el nombre Char .

Si un programador asume que character significa la representación de un conjunto de rasgos mentales y éticos utilizados en la narración de historias, eso no se considera un problema con el nombre character .

Tenga en cuenta que todos estos son asuntos de texto/lingüísticos. Todos ellos tienen otros significados. Y, sin embargo, los programadores se han aclimatado muy bien. Esos términos se han convertido en estándares de facto, debido a una convención establecida en el campo: nuestra jerga. Existe un precedente establecido de que los programadores _son_ lo suficientemente inteligentes como para seguir esto.

Usted mismo estaba argumentando que los dos se alinearon lo suficientemente cerca como para que no fuera un problema.

Sí, esto es GitHub. En un tema ya cerrado, en el que solo estaba agregando mis pensamientos sobre por qué sentí que Rune estaba bien porque había un precedente establecido en el nombre. Este no es el lugar ni el contexto para escribir un tratado, lleno de definiciones extensas y palabras cuidadosamente elegidas. Por ejemplo, si estoy poniendo un PR para, digamos, un decodificador UTF-8, no voy a describir explícitamente por qué implementé el DFA de Hoehrmann sobre enfoques alternativos. Solo diré "aquí está, aquí hay algunas pruebas de que funciona, aquí hay algunos puntos de referencia que respaldan por qué elegí esto".

Mi argumento es que la gente lo confundirá con glifos, grafemas y grupos de grafemas.

No están confundiendo ninguno de los mencionados, ni Tree , Heap , Table , Key , Socket , Port ...

Este es un argumento extremadamente falso. Un trozo de hilo y una cadena de texto no se confunden fácilmente. Una planta alta y una estructura de datos de árbol no se confunden fácilmente. Un punto de código, por otro lado, es un concepto muy mal entendido por la mayoría de los programadores y constantemente confundido con todos los otros conceptos que hemos discutido. La solución a esto es, como dices, leer la documentación. Sin embargo, un idioma que usa su propio nombre "inteligente" para los puntos de código hace que sea aún más difícil aplicar el conocimiento de la _documentación real de Unicode_ a ese idioma. Y eso me lleva a esto:

Esos términos se han convertido en estándares de facto, debido a una convención establecida en el campo: nuestra jerga.

Y este es el quid de todo. Parece que está afirmando que "runa" es un término bien establecido para un punto de código que se entiende ampliamente en la programación, o debería serlo. Si es lo primero, lo invito a que le pregunte a un programador promedio con experiencia en un lenguaje de programación importante que no sea Go si alguna vez lo ha escuchado. Si es lo último, entonces le preguntaría cuál es el punto de competir con la terminología oficial de Unicode en una situación ya confusa y mal entendida que con frecuencia es malinterpretada incluso por desarrolladores con mucha experiencia.

Aporte externo de @Entomy : todo su argumento, por lo que puedo decir, es 'es confuso y malo, sí, pero no es tan confuso y malo'.
¿Entonces? ¿Por qué no puede ser realmente bueno en su lugar? ¿Cuál es el problema de nombrarlo exactamente como lo nombra Unicode?
Además, las runas no son puntos de código, ni siquiera grafemas o grupos, en el campo general de la informática. Si busca 'runas Unicode' en Google, cualquier cosa que las relacione con puntos de código no aparece hasta la página 2, e incluso entonces son solo enlaces godoc / Nim. Incluso en DuckDuckGo, con el que los programadores pueden sentirse más cómodos, sigue siendo un resultado de la página 2. Entonces, el único argumento que queda para el nombre que he visto es que es intuitivo que representa un punto de código, pero no lo es. Es intuitivo que representa un grupo de grafemas, o quizás solo un grafema.
Fuente: he usado Go y pensé que era un grafema hasta cuatro años después cuando leí este número hace un momento.

(y decir que está bien que sugiera un grafema porque está 'lo suficientemente cerca' me recuerda que el carácter de 16 bits está lo suficientemente cerca).
Sí, si los programadores fueran más inteligentes y leyeran más documentación, no necesitaríamos un nombre significativo para él, ni siquiera un tipo. La gente simplemente sabría pasar puntos de código en un int around en lugar de char. Pero no lo son. Son tan inteligentes como lo son ahora, y eso no va a cambiar solo porque se agregó otra API. El objetivo es aumentar la cantidad de software que maneja correctamente otros idiomas además del inglés, no solo introducir nuevas formas de hacer lo mismo y mantener las mismas barreras de entrada que antes.

Solo por el bien del argumento y con fines científicos, me gustaría señalar a todos aquí el lenguaje de programación que maneja mejor el texto Unicode, donde "mejor" se define como "más cercano de acuerdo con el estándar Unicode", no fingiendo simplicidad: Swift

  • String es un búfer de texto Unicode arbitrario.
  • Character , sobre el que itera y lo que no, no es un único valor escalar Unicode, sino un clúster de grafema extendido. Vea este ejemplo para el grupo de grafemas : let decomposed: Character = "\u{1112}\u{1161}\u{11AB}" // ᄒ, ᅡ, ᆫ
  • Si necesita valores escalares Unicode, también puede iterarlos. Su tipo se llama UnicodeScalar .
  • Y si realmente tiene ganas de necesitarlo, también puede iterar sobre unidades de código UTF-8 y UTF-16, produciendo UInt 8 s y UInt 16 s.

Ahora, no estoy sugiriendo que C# use el estilo completo de Swift. Si bien esto sería increíble, también se necesitan muchos cambios y trabajo. Sin embargo, estoy aquí para sugerir elegir nombres de estilo Swift, por todas las razones que señaló @Serentty , y dejar la opción abierta para convertir las cadenas de texto en estilo Swift eventualmente.

Algunos posibles nombres mejores que Rune : CodeUnit32 , UnicodeScalar , CodeUnit , UniScalar , UnicodeValue , UniValue , UnicodeScalarValue . Creo que los dos primeros podrían encajar perfectamente en las convenciones de nomenclatura de C#. Tenga en cuenta que UnicodeScalar es objetivamente el mejor nombre, ya que las unidades de código son solo formas de codificar un valor escalar Unicode en la jerga Unicode. Entonces CodeUnit32 implica iterar sobre las unidades de código de una cadena de texto codificada en UTF-32, mientras que UnicodeScalar es independiente de la codificación.

Editar: Sí, el nombre System.Rune ya está disponible. Todo esto es solo un "si queremos hacerlo mejor antes de que esto tenga media década".

@sabor-de-pastel

todo su argumento, por lo que puedo decir, es 'es confuso y malo, sí, pero no es tan confuso y malo'.

No, ese no es mi argumento en absoluto. Estoy haciendo lo mejor que puedo con la discapacidad que tengo, pero esta no es mi intención de comunicación.

Si busca 'runas Unicode' en Google, cualquier cosa que las relacione con puntos de código no aparece hasta la página 2, e incluso entonces son solo enlaces godoc / Nim.

Si busca 'cadena Unicode' en Google, tampoco obtendrá específicamente cómo funcionan las cadenas .NET. Se trata de buscar una cosa adyacente. Como una analogía muy estricta, programo tanto en .NET como en Ada; string no es lo mismo entre ellos, y una ligera lectura para cada uno es una buena idea.

Las definiciones sobrecargadas no son inusuales en el lenguaje y, sin embargo, nos las arreglamos bien. Puede que le sorprenda, pero "ejecutar" tiene al menos 179 definiciones formales, "tomar" tiene al menos 127, "romper" tiene al menos "123", y así sucesivamente. [ fuente ] Las personas son increíblemente capaces y pueden navegar con éxito en una complejidad mucho mayor de lo que se considera problemático aquí. La preocupación de que "runa" tenga al menos 2 definiciones formales, en mi opinión, no está justificada cuando se puede demostrar que las personas manejan más de 50 veces las sobrecargas.

Además, esto es una explotación flagrante del comportamiento de los motores de búsqueda. Con la mayoría de los motores de búsqueda, obtiene resultados en función de cuántas páginas se vinculan a algo. También hay otros factores, y cada enfoque pondera las cosas de manera diferente. Como .NET Rune es un concepto bastante reciente en comparación, habrá mucho menos contenido hablando de él y se necesitarán más páginas para llegar a él. Pero también está usando la herramienta de búsqueda incorrecta. Si quiero encontrar investigaciones sobre algoritmos de búsqueda de cadenas, para ver si ha surgido algo nuevo en los últimos años, no busco en Google ni en DDG. Semantic Scholar, Google Scholar y otros son mejores puntos de partida. De manera similar, si desea comprender cosas sobre las API de .NET, busque primero MSDocs. Si me quejo de que "momento de inercia", un término de física/ingeniería, es vago o engañoso en su nombre, y debería renombrarse porque no puedo encontrar ninguna información al respecto en los primeros libros, comenzando por el número más bajo en una biblioteca que usa la clasificación decimal de Dewey, eso no es un problema con la denominación de "momento de inercia"; Claramente estoy buscando en el lugar equivocado.

Fuente: he usado Go y pensé que era un grafema hasta cuatro años después cuando leí este número hace un momento.

Revisé los documentos de Go y las notas de la versión, al menos las que pude encontrar, y tengo que estar de acuerdo contigo. Son muy vagos acerca de lo que es rune y, lamentablemente, incluso son vagos acerca de qué tan grande es rune . Sospecho que esta vaguedad causará problemas más adelante, ya que he visto a Ada ser igualmente imprecisa acerca de las restricciones de tipos de datos y que se muerda a sí misma años después.

Sin embargo, debo decir que msdocs hace un trabajo mucho mejor con una descripción muy detallada y concisa.

Representa un valor escalar Unicode ([ U+0000..U+D7FF ], inclusive; o [ U+E000..U+10FFFF ], inclusive).

Habiendo dicho esto, los comentarios son algo deficientes y sería beneficioso explicar por qué existe Rune y cuándo querría usarlo (y también el lugar apropiado para una explicación más detallada que la simplificada mencionada anteriormente) . Voy a presentar algunas mejoras allí.

@Evrey

Solo por el bien del argumento, y con fines científicos, me gustaría señalar a todos aquí el lenguaje de programación que maneja mejor el texto Unicode.

Esta es una opinión. Uno con el que estoy absolutamente de acuerdo; Swift ciertamente maneja mejor el UNICODE moderno. Pero sin una cita de investigación reproducible revisada por pares que confirme estos resultados, esto no es una afirmación científica.

Ahora, no estoy sugiriendo que C# use el estilo completo de Swift. Si bien esto sería increíble, también se necesitan muchos cambios y trabajo.

Y rompería el software existente.

deje la opción abierta para convertir las cadenas de texto en estilo Swift eventualmente.

Y rompería el software existente.

Sí, el nombre System.Rune ya existe. Todo esto es solo un "si queremos hacerlo mejor antes de que esto tenga media década".

Y rompería el software existente.

Como hipotético, si se hicieran cambios en el nombre existente, ¿cómo propone que el software existente dirigido a .NET Core 3.0/3.1, donde Rune ya está en uso, siga siendo compatible, al mismo tiempo que existe como un nombre diferente en tiempos de ejecución de destino posteriores?

Y rompería el software existente.

Como mencioné, solo estoy argumentando desde la perspectiva de los principios y el idealismo. La realidad de las cosas ha sido mencionada abundantemente. Aunque hay algunos matices en todo eso:

  • Ir al estilo Swift con cadenas no necesariamente rompe el software. Es solo cuestión de agregar más métodos y tipos de enumeración además de la interfaz String ya existente. No me refiero a cosas radicales como cambiar System.Char en un tipo de grupo de grafema o algo así.
  • Si un nombre de tipo existente como System.Char se reutilizara para un tipo diferente, entonces sí, sería un gran cambio. Y un cambio irresponsable en eso. Estoy contigo allí.
  • Un .NET Core 4.0 hipotético, hablando en SemVer, puede hacer lo que quiera. Aparte de eso, los cambios hasta un hipotético 4.0 no dan tanto miedo: convertir System.Rune en un alias de tipo obsoleto para System.UnicodeScalar o como se llame. El software que usa Rune no notará la diferencia, aparte de una nota de obsolescencia, y el nuevo software puede usar el tipo real con mejor nombre. Y un 4.0 hipotético luego simplemente cae Rune .
  • Del mismo modo, System.Char podría convertirse en un alias para System.CodeUnit16 o algo así.
  • Hacerlo al estilo Swift entonces solo significa agregar System.GraphemeCluster a la mezcla.
  • La introducción de más alias de palabras clave nuevos para todos estos tipos puede ser problemática.

Solo dejando comida para el pensamiento aquí. Creo que System.Rune , si bien es un nombre de tipo incorrecto para su propósito, en realidad no empeora el statu quo de nombres anterior. Creo que es genial que finalmente haya un tipo adecuado capaz de codificar todos los escalares Unicode. Sin embargo, veo una gran oportunidad para difundir una tendencia de manejo y nombres Unicode más precisos. Una oportunidad que todos aquí son libres de dejar de lado.

Hola a todos: el nombre System.Text.Rune es lo que se envió y lo que usaremos en el futuro. Hubo una discusión significativa (¡y acalorada!) anterior sobre el uso del nombre UnicodeScalar en lugar de Rune , pero al final ganó Rune . El equipo no está considerando la idea de elegir un nombre diferente en este momento. Y aunque sé que a la gente le apasiona esto y continuaremos monitoreando la conversación aquí, en última instancia, tenga en cuenta que cualquier energía gastada en continuar con el litigio del tema de los nombres no generará dividendos.

Para aclarar, y según los documentos: el tipo System.Text.Rune en .NET es exactamente equivalente a un valor escalar Unicode. Esto se hace cumplir por la construcción. Esto lo hace más análogo al tipo UnicodeScalar rune Go.

Hay un esfuerzo en marcha para agregar una sección a los documentos Rune que detalla sus casos de uso y cómo se relaciona con otras API de procesamiento de texto en .NET y conceptos en Unicode. El problema de seguimiento está en https://github.com/dotnet/docs/issues/15845. También hay un enlace de ese problema de seguimiento a un borrador actual de los documentos conceptuales.

Para mí, el principal inconveniente de UnicodeScalar es la gran disparidad entre la longitud del nombre del tipo y el tamaño de datos del tipo. Esencialmente es un int con algunas lagunas en su dominio.

Sin embargo, la verbosidad en el uso sería extrema:

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

frente al equivalente char sobre string (e idealmente la gente usaría el nuevo tipo sobre char ya que son valores enteros en lugar de contener valores divididos)

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

Rune es un compromiso en la verbosidad del nombre de tipo:

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

@GrabYourPitchforks

¡Hola! Para ser honesto, me quedé atrapado en este argumento no porque esté tratando de convencer a la gente de .NET de que se debe cambiar el nombre, ya que parece que ese barco ha zarpado, sino simplemente porque quería expresar mi opinión a otros en este hilo que no estuvieron de acuerdo con él. Creo que es maravilloso que C# finalmente tenga un tipo de carácter _real_ en lugar del tipo de carácter roto que ha tenido durante tanto tiempo, y el nombre es completamente secundario. Entiendo que se debe lograr un gran equilibrio entre la brevedad y la precisión, y aunque yo habría colocado el punto óptimo en algún lugar alrededor CodePoint , entiendo por qué otros no estarían de acuerdo.

Pero nuevamente, ¡quiero agradecerles por todo el arduo trabajo en la modernización del soporte Unicode de .NET! Esto es algo que marca una gran diferencia para muchas personas en todo el mundo.

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

bencz picture bencz  ·  3Comentarios

EgorBo picture EgorBo  ·  3Comentarios

GitAntoinee picture GitAntoinee  ·  3Comentarios

chunseoklee picture chunseoklee  ·  3Comentarios

sahithreddyk picture sahithreddyk  ·  3Comentarios