Runtime: Apresentando System.Rune

Criado em 16 set. 2017  ·  106Comentários  ·  Fonte: dotnet/runtime

Inspirado pela discussão aqui:

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

Um dos desafios que o .NET enfrenta com seu suporte a Unicode é que ele está enraizado em um design hoje obsoleto. A forma como representamos os caracteres em .NET é com System.Char que é um valor de 16 bits, insuficiente para representar valores Unicode.

Os desenvolvedores .NET precisam aprender sobre os pares substitutos arcanos:

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

Os desenvolvedores raramente usam esse suporte, principalmente porque não estão familiarizados o suficiente com o Unicode e muito menos com o que o .NET tem a oferecer para eles.

Proponho que introduzamos um System.Rune que é apoiado por um inteiro de 32 bits e que corresponde a um codePoint e que mostramos em C# o tipo rune equivalente para ser um alias para esse tipo.

rune se tornaria o substituto preferencial para char e serviria como base para o Unicode adequado e manipulação de strings em .NET.

Quanto ao porquê do nome runa, a inspiração vem de Go:

https://blog.golang.org/strings

A seção "Codificar pontos, personagens e runas" fornece a explicação, uma versão curta é:

"Code point" é um bocado, então Go introduz um termo mais curto para o conceito: runa. O termo aparece nas bibliotecas e no código-fonte e significa exatamente o mesmo que "ponto de código", com uma adição interessante.

Atualização Agora tenho uma implementação de System.Rune aqui:

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

Com a seguinte 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
    }
}

Atualizar problemas conhecidos

  • [x] Algumas APIs acima pegam uma uint, precisam pegar uma Rune.
  • [ ] Necessidade de implementar a família IComparable
  • [ ] RuneCount/RuneLen precisa de nomes melhores, veja os documentos (eles deveriam ser talvez Utf8BytesNeeded?)
  • [ ] Acima, as APIs "ustring" fazem referência à minha API UTF8, isso realmente não faz parte da API, mas devemos considerar se há um gateway para System.String em algumas delas ou para Utf8String.
api-needs-work area-System.Runtime up-for-grabs

Comentários muito úteis

Eu disse isso na edição original e vou dizer de novo. Abandonar o que um padrão diz porque você não gosta da frase vai confundir mais do que vai resolver, e, dado que existe uma página de código de runa em Unicode, isso só confunde mais.

O nome está errado.

Todos 106 comentários

Você espera que a representação na memória seja strings de objetos de 32 bits ou traduzida em tempo real? E sobre a duplicação de memória se o primeiro? Qual é o impacto no desempenho se o último?

É uma boa ideia nomear uma tecnologia relacionada a Unicode após um script específico suportado por Unicode (e uma tecnologia para melhorar o suporte ao plano astral após um script BMP)?

Acho que a proposta (e talvez precise ser mais explícita) é que a representação de strings na memória não mude em nada. O tipo Rune representa meramente um ponto de código individual distinto de 21 bits (armazenado como um inteiro de 32 bits). Métodos que se referem a pontos de código podem retornar um Rune . Presumivelmente, há alguma funcionalidade em string que permite enumerar Rune 's.

Acho que há alguns pontos óbvios sobre os quais precisamos obter consenso para algo assim:

  1. Existe um valor significativo em criar um tipo Rune em vez de usar Int32 como os métodos atuais fazem?
  2. A palavra "runa" é realmente uma boa escolha?

Para responder (1), acho que precisamos de uma descrição mais completa de como Rune seria exposto, quais métodos o receberiam e retornariam, etc. E para determinar se isso é melhor do que lidar com Int32 em vez disso.

Quanto a (2), estou um pouco hesitante. "Rune" é uma palavra meio esotérica em inglês e tem algumas conotações incomuns para seu uso neste contexto. Há também o ponto que outros estão trazendo: colide com outro conceito Unicode. Quando faço uma pesquisa por "Unicode Rune", recebo principalmente resultados para o bloco Runic Unicode e apenas algumas documentações da linguagem Go.

char é meia palavra e também uma palavra inteira; e você tem que inspecionar seus arredores para determinar qual - como a corrente representa meia letra ou uma letra inteira.

Talvez System.character onde é sempre uma letra inteira... :sunglasses:

char é uma representação um pouco terrível e mesmo para idiomas somente ascii/latin; a ascensão dos emojis ainda vai permear; significa que char é um cheque e talvez verifique o próximo tipo char

@NickCraver no Twitter

Enquanto utf8 é uma codificação de largura variável; é raro (se for o caso?) que um usuário queira lidar com meios caracteres; tanto para utf8 quanto para utf32.

Um tipo de 32 bits funcionaria bem para enumeração.

Mais difícil seria indexOf, Length etc para uma perspectiva de desempenho ou memória.

  1. matriz de bytes é a melhor representação para um formato opaco; por exemplo, manter o formato em seu formato original ou em um formato final (transferência de arquivos, transferência eletrônica, etc.)
  2. matriz de bytes é a melhor representação para largura de banda de memória e tamanho de memória
  3. matriz de bytes é consistente com Position e indexOf, Length etc em termos de bytes

No entanto, quando você começa a se importar com personagens reais, maiúsculas, dividindo em personagens; entendendo o que é um caractere, o byte se torna uma largura variável. Char não torna isso realmente melhor; dobra o tamanho dos caracteres menores; inclui mais caracteres, mas ainda tem largura variável.

Para isso, um valor de 32 bits pode ser muito útil do ponto de vista do código do usuário. No entanto, ele tem problemas com posição, comprimento e itens secundários (indexOf etc)

Estou muito interessado em uma string somente ascii e uma string utf8 "Implementação de string compacta" https://github.com/dotnet/coreclr/issues/7083; para processamento rápido de strings somente ASCII

No entanto, indo contra tudo que eu estava argumentando lá... Gostaria de saber como seria uma representação de 32 bits de utf8? A posição seria mapeada para a posição; procurar caracteres seria rápido como é em ascii, os itens estão em tamanhos nativos, etc., como isso se compara ao processamento de cada byte ou caractere para determinar seu tamanho?

A conversão de e para seria mais cara; então seria mais um formato de processamento; do que um formato de armazenamento.

@migueldeicaza como eu entendo e você está se referindo apenas à expansão do formato de caractere único de 16 bits para 32 bits para que todas as representações estejam contidas no valor; em vez da possibilidade de um meio-valor - em vez de necessariamente o formato interno.

No entanto, algumas coisas a considerar (ou seja, relação de posição e custo de busca, etc.)

Além: Swift também lida com formatos de personagens inteiros

O Swift fornece várias maneiras diferentes de acessar representações Unicode de strings. Você pode iterar sobre a string com uma instrução for-in, para acessar seus valores de caracteres individuais como clusters de grafema estendidos Unicode. Este processo é descrito em Trabalhando com Personagens.

Como alternativa, acesse um valor String em uma das três outras representações compatíveis com Unicode:

  • Uma coleção de unidades de código UTF-8 (acessadas com a propriedade utf8 da string)
  • Uma coleção de unidades de código UTF-16 (acessadas com a propriedade utf16 da string)
  • Uma coleção de valores escalares Unicode de 21 bits, equivalentes ao formulário de codificação UTF-32 da string (acessado com a propriedade unicodeScalars da string)

Eu disse isso na edição original e vou dizer de novo. Abandonar o que um padrão diz porque você não gosta da frase vai confundir mais do que vai resolver, e, dado que existe uma página de código de runa em Unicode, isso só confunde mais.

O nome está errado.

@mellinoe

A Runa forneceria muitas das operações que hoje você espera em um Char, como ToLower[Invariant], ToUpper[Invariant], ToTitle, IsDigit, IsAlpha, IsGraphic, IsSymbol, IsControl.

Além disso, forneceria coisas como:

  • EncodeRune (codifica uma runa em um buffer de bytes)
  • RuneUtf8Len (retorna o número de bytes necessários para codificar a runa em UTF8),
  • IsValid (nem todos os valores Int32 são válidos)

E interoperabilidade para string e Utf8string conforme necessário.

Eu portei/ajustei o suporte de string Go para .NET, e ele oferece uma visão de como seria esse mundo (isso é sem nenhuma ajuda de tempo de execução):

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

@benadams disse:

Gostaria de saber como seria uma representação de 32 bits de utf8? A posição seria mapeada para a posição; procurar caracteres seria rápido como é em ascii, os itens estão em tamanhos nativos, etc., como isso se compara ao processamento de cada byte ou caractere para determinar seu tamanho?

UTF8 é uma representação na memória, que continuaria a existir e continuaria a ser a representação (e esperamos que esta seja a codificação interna de longo prazo para futuras strings em .NET).

Você decodificaria as strings UTF16 existentes (System.String) ou as próximas strings UTF8 (Utf8String) não em Chars (pela razão que você e eu concordamos), mas em Runes.

Alguns exemplos, convertem uma string Utf8 em runas:

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

Uma string utf8 contém uma runa:

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

Acabei de notar que não implementei o indexador ("Get me the n-th rune")

A velocidade de acesso à enésima runa em uma sequência é uma função do armazenamento, não da própria runa. Por exemplo, se seu armazenamento for UTF32, você terá acesso direto a cada runa. Isso é acadêmico, pois ninguém usa isso. O acesso ao elemento Nth em UTF16 e UTF8 requer a verificação adequada dos elementos que compõem a string (bytes ou inteiros de 16 bits) para determinar o limite correto. Não deve ser confundido com String[int n] { get; } que apenas retorna o n-ésimo caractere, independentemente da correção.

@benaadams O personagem Swift está um nível acima de uma runa. Caracteres em swift são "grupos de grafema estendidos" que são compostos de uma ou mais runas que, quando combinadas, produzem um caractere legível por humanos.

Portanto, o caractere Swift não tem um tamanho fixo de 32 bits, é de tamanho variável (e também devemos ter essa construção, mas que pertence a um tipo de dados diferente). Aqui está o exemplo dessa página, mas isso também se estende à definição da tonalidade de um emoji:

Aqui está um exemplo. A letra é pode ser representada como o único escalar Unicode é (LATIN SMALL LETTER E WITH ACUTE, ou U+00E9). No entanto, a mesma letra também pode ser representada como um par de escalares - uma letra padrão e (LATIN SMALL LETTER E, ou U+0065), seguida pelo escalar COMBINING ACUTE ACCENT (U+0301). O escalar COMBINING ACUTE ACCENT é aplicado graficamente ao escalar que o precede, transformando um e em um é quando é renderizado por um sistema de renderização de texto com reconhecimento de Unicode.

Só para mim a palavra grapheme seria mais auto-descritiva.

Meus dois centavos sobre o nome, citando novamente o post Go on strings com ênfase:

" Code point " é um bocado, então Go introduz um termo mais curto para o conceito: runa. O termo aparece nas bibliotecas e no código-fonte e significa exatamente o mesmo que "ponto de código" , com uma adição interessante.

Eu concordo 100% com @blowdart , chamá-lo de runa é apenas confuso e errado. O código de menção padrão unicode aponta três vezes apenas na primeira página do capítulo de introdução, mas o termo runa não aparece em nenhum lugar.

Se for um ponto de código, então deve ser chamado de ponto de código , simples assim.

Se o termo runa nunca apareceu no padrão , tudo bem, o problema é que ele aparece várias vezes no capítulo 8, em relação às runas. Não está apenas errado, está ativamente confundindo o assunto com outro.

Só para mim a palavra grapheme seria mais auto-descritiva.

Se isso for sobre pontos de código de 32 bits, o termo grapheme seria confuso porque um grafema é outra coisa novamente.

Eu sempre quis um tipo de dados de ponto de código (não em um bom tempo, pois o que eu trabalhei mudou, mas há alguns anos eu queria muito isso e escrevi soluções parciais sobrepostas para partes dessa necessidade e poderia ter feito com uma biblioteca bem testada). Não vejo por que isso não deveria ser chamado de algo como CodePoint . A maioria das pessoas que percebem que precisam de tal tipo provavelmente estaria pensando em termos de pontos de código de qualquer maneira, não em termos de runas; ou então em termos de code-points e runas como partes separadas de sua tarefa. ᚱᚢᚾᚪ ᛒᛇᚦ ᛥᛁᛚᛖ ᛒᚱᚣᚳᛖᚢ/rúna béoþ stille bryceu/runes ainda são usados. Eu só preciso usar runas uma vez por ano, e geralmente com pergaminho e tinta em vez de qualquer coisa digital, mas certamente há pessoas que lidam com elas digitalmente também. (Mesmo com dados do século 20, conheço um caso em que eles estão em uso no arquivamento de dados da era da Segunda Guerra Mundial).

Grafema é ainda mais complicado, uma vez que muitas vezes queremos ir octetos → chars (bem manipulados pelo .NET já) então chars → code-points, e então code-points → graphemes.

sinalizando isso como pendente por enquanto.

Próximos passos : O que estamos procurando é: uma proposta formal que incluirá o feedback de cima (a nomenclatura real do tipo e as vantagens de usar isso em vez de usar apenas um Int32).

Atualizei o problema, tanto com a API proposta quanto com uma implementação inicial:

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

Quanto à nomenclatura do tipo, é uma questão de ter um local onde você possa procurar as operações válidas no tipo, bem como ter recursos específicos do tipo (consulte a implementação para alguns exemplos).

@migueldeicaza antes de sinalizar como pronto para revisão, quais são seus pensamentos sobre as preocupações sobre a nomenclatura real do tipo, você acha que talvez o CodePoint possa ser melhor em termos de descrever qual é o tipo?

Eu acho que o argumento para usar codepoint como um nome é fraco.

Usá-lo é uma idéia terrível, a longo prazo, isso precisa substituir cada uso de "char" no código existente - se esperamos obter suporte adequado a Unicode.

Eu gostaria que pudéssemos ter usado "char" como Rust faz, mas, infelizmente, já o pegamos e temos um quebrado.

Ir ter abraçado este nome é um bom precedente.

Concordo que code point não é o termo correto para usar aqui. No mínimo, com base no padrão Unicode, não inclui valores acima de 10FFFF (http://unicode.org/glossary/#code_point).

Eu não gosto do termo rune . Eu acho que tem um uso existente em Unicode e em outros lugares que só causará confusão geral. Eu também acho que tem uma boa chance de entrar em conflito com os tipos de usuário existentes (especialmente para coisas como Unity, onde uma 'Runa' pode representar um objeto de jogo específico).

No entanto, gosto da ideia de um tipo que cubra o tipo C++ 11 char32_t , apenas com um nome diferente.

Há algo a ser dito sobre Char32 . É direto ao ponto, é análogo aos nomes de tipo dos tipos integrais. Ele fala no nível conceitual do personagem, em vez do nível de ponto de código. Não é o nome de um script.

Já que estamos olhando para ter nint que tal nchar ?

O precedente estaria nos bancos de dados nchar e nvarchar

Onde nchar são caracteres nacionais / caracteres nacionais e nvarchar são caracteres nacionais variados / caracteres nacionais variados; quais são os tipos de campo nos quais você pode armazenar unicode, também alguns padrões ISO - não tenho certeza de qual, talvez SQL?

O que é esse uso Unicode de runa? Isso é novidade para mim.

U+16A0 a U+16F8

É usado para se referir a uma página de código específica no padrão Unicode. Ele foi mencionado algumas vezes neste tópico: http://unicode.org/charts/PDF/U16A0.pdf

Ah rúnico, não rúnico.

O nome de apoio (System.Rune ou System.Char32) não é tão importante quanto o rótulo que será projetado em C#.

Em primeiro lugar: sim, sim, e mais disso, por favor. Eu amo essa ideia (sinceramente, eu tenho uma ideia semelhante há muito tempo). Na verdade, estamos usando uma classe de string personalizada e uma estrutura de caracteres em nossa compatibilidade com Git posteriormente no Visual Studio por um tempo (o Git fala em Utf-8 e a transcodificação é muito lenta).

No tópico de nomes de métodos estáticos, podemos evitar nomes curtos arbitrários, por favor? Dado que Char.IsPunctuation é o método atual, podemos espelhar isso com Rune.IsPunctuation ou similar?

Supondo (sempre perigoso) que isso seja aceito, podemos ter uma rune intrínseca ou c32 , ou apenas substituir char completamente pela implementação System.Rune ?

Eu sugiro unichar ou uchar embora uchar pareça um caractere não assinado. Seja qual for o escolhido, no entanto, espero que tenhamos um alias de idioma específico para ele. Pessoalmente, sou um grande fã de usar os aliases de linguagem para tipos primitivos.

Também concordo com @whoisj - Definitivamente preferiria nomes de métodos completos sobre curtos/abreviações.

Também concordo com @whoisj - Definitivamente preferiria nomes de métodos completos sobre curtos/abreviações.

IMO uma linguagem (e suas bibliotecas) precisa escolher nomes completos e abreviados, ou se concentrar nas abreviações (como C com strcmp, memcpy, etc.)

ou apenas substituir char completamente com a implementação System.Rune ?

Isso seria uma mudança de ruptura por razões bastante óbvias.

Isso seria uma mudança de ruptura por razões bastante óbvias.

Meus comentários foram principalmente de língua e bochecha, e esperançosos. Um tipo de 16 bits para caractere foi um erro desde o início.

Boa pegada na nomenclatura, vai corrigir.

Existem outras pequenas inconsistências na API fornecida, veja como corrigi-las também.

@migueldeicaza

Ah rúnico, não rúnico.

Runic é o adjetivo, rune o substantivo. Todos os personagens rúnicos são runas.

_Runic_ é o adjetivo, _rune_ o substantivo. Todos os personagens rúnicos são runas.

Por mais justo que pareça "Cortana: defina _'rune'_" surge com:

uma letra de um antigo alfabeto germânico, relacionado ao alfabeto romano.

Ah sim, sempre que vejo a palavra "rune", penso imediatamente neste capítulo obscuro sobre uma especificação que ninguém leu que fala sobre "The Runic Unicode Block".

😆 Penso nas memórias de infância de ler Tolkien.

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

Sim, não penso especificamente na especificação, mas penso no tipo de personagens a que a especificação se refere.

Você diz rune e eu penso em magia, fantasia, quebra-cabeças enigmáticos, línguas antigas, etc.

Fico feliz que você não veja a palavra "rune" e imediatamente pense "Ah, isso claramente se refere ao bloco rúnico Unicode 7.0, cujo valor será limitado a esses valores exclusivos no intervalo 16A0..16F8".

Eu sei que Tanner é uma única voz aqui, e alguns de vocês ainda estão pensando "Mas Miguel, eu vejo a palavra 'rune' e imediatamente penso em um tipo de dados que poderia conter apenas 88 valores possíveis". Se esse é um problema com o qual você está lutando, meu irmão/irmã, tenho uma novidade para você: você tem peixes maiores para fritar.

Eu tenho acompanhado este tópico por um tempo com uma mistura de entusiasmo e hesitação por pouco mais de um mês. Participei da Internationalization and Unicode Conference no mês passado, e nenhuma das apresentações tratou de .NET. Há um problema de percepção com o .NET Framework; um que não é necessariamente imerecido, dada a história de suas características de globalização. Dito isso, adoro programar em C# e quero muito ver novos recursos que reforcem o lugar do .NET em uma comunidade verdadeiramente global. Acho que esta proposta é um bom passo nessa direção de abraçar os padrões que a comunidade de internacionalização espera do software.

Minha hesitação foi principalmente por causa das brigas sobre o nome do tipo. Embora seja verdade que os designers do Go escolheram o nome "runa", isso é problemático pelo motivo listado acima repetidamente: existem pontos de código que são propriamente chamados de runas. É difícil para mim concordar com uma proposta que tenta seguir um padrão respeitado e depois redefine a terminologia que faz parte da especificação. Além disso, o argumento de que a maioria dos desenvolvedores ignora o termo é ilusório, uma vez que os desenvolvedores mais interessados ​​em usar esse tipo corretamente são mais propensos a entender a especificação Unicode e ter uma boa ideia do que realmente é uma "runa". Imagine a estranheza que poderia existir se você misturasse a terminologia:

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

Claro, eu tomei o caminho mais fácil aqui, criticando sem fornecer um novo nome. Acho que a sugestão anterior de CodePoint é a opção mais autodescritiva (e aparece na descrição original do problema), mas char32 teria mais paridade com os tipos primitivos existentes (embora eu hesite em dizer que nem todo ponto de código é um caractere). Se o objetivo é construir um melhor suporte a Unicode no .NET, sou absolutamente favorável a esse caminho, mas a melhor maneira de fazer isso é seguir as especificações.

Três sugestões:

  1. A classe Rune está faltando o crítico "IsCombining". Sem isso, não podemos converter de uma série de runas (pontos de código) em uma série de grafemas.
  1. Eu adoraria ter também uma classe Grapheme correspondente. Um grafema neste contexto é realmente apenas uma lista de uma ou mais Runas (Code Points) de tal forma que a primeira runa não está combinando e o resto das runas estão combinando. O caso de uso é para quando um desenvolvedor precisa lidar com pedaços de "caracteres visíveis". Por exemplo, a + GRAVE são duas runas que formam um grafema.

  2. Na rede, muitas vezes obtemos um pedaço de bytes que precisamos transformar em um objeto tipo "string" onde os bytes podem não estar completos (por exemplo, somos informados de alguns bytes, mas o último byte em uma sequência de vários bytes não ainda não chegou). Não vejo nenhuma maneira óbvia de converter um fluxo de bytes em um fluxo de runas de modo que perder o último byte de uma sequência de vários bytes seja considerado uma situação normal que será corrigida quando recebermos o próximo conjunto de bytes.

E por último, use nomes Unicode e chame isso de CodePoint. Sim, o consórcio Unicode faz um péssimo trabalho ao explicar a diferença. Mas a solução é adicionar documentação clara e utilizável; qualquer outra coisa confunde a questão em vez de ajudar a esclarecer.

Eu não sei por onde começar na solicitação de combinação, nem Go, Rust ou Swift exibem tal API em rune, Character ou Unicode Scalar (seus nomes para System.Rune ). Forneça uma implementação proposta.

Em clusters de grafema, é uma boa ideia, deve ser rastreado independentemente de System.Rune . Por que vale a pena, o Swift usa Character para isso, mas também o Swift não é um ótimo modelo para lidar com strings.

Transformar streams de bytes em uma runa adequada é um problema que pertence a uma API de nível superior. Dito isso, você pode ver minha implementação ustring que usa o mesmo substrato que minha implementação System.Rune para ver como esses buffers são mapeados em strings utf8:

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

Documentação, que ainda não atualizei desde que introduzi System.Rune na API, mas abrange:

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

Quanto ao nome, claramente Rust é o melhor com char , mas nós estragamos isso. O segundo melhor é Go with rune . Qualquer coisa maior que quatro caracteres será apenas um incômodo para as pessoas fazerem a coisa certa.

Eu sinto Muito; Acho que CodePoint é um nome excepcionalmente bom. É autoexplicativo, memorável e autocompleta com c p .

IsCombining definitivamente seria necessário, mas também é conhecer a classe de combinação e uma vez que temos que IsCombining é em grande parte açúcar, pois é apenas IsCombining => CombiningClass != 0 ou IsCombining => CombiningClass != CombiningClass.None . Clusters de grafemas estariam fora dele novamente, mas o ponto de partida seria conhecer a classe de combinação para clustering padrão, reordenação, etc.

CodePoint é um ótimo nome para um tipo sobre pontos de código, e quatro caracteres dificilmente é um limite com o qual temos que lidar com outros tipos muito usados; string é 50% maior e não nos impede de usá-lo regularmente. Quatro letras escolhidas aleatoriamente seriam um nome melhor do que repetir o erro de Go.

Como uint não é compatível com CLS, não há nenhum diretor compatível com CLS que cubra os planos astrais. int também seria necessário.

Conversões implícitas bidirecionais podem levar a coisas ruins acontecendo com sobrecargas, então uma direção talvez deva ser explícita. Não está claro qual. Por um lado uint / int é mais largo do que os pontos de código, pois os valores abaixo de 0 ou acima de 10FFFF 16 não são significativos, e ter essa conversão implícita permite o uso mais rápido de mais APIs existentes para números. Por outro lado, vejo querer converter de um número para um ponto de código com mais frequência do que o contrário.

Como o uint não é compatível com CLS, não há nenhum ctor compatível com CLS que cubra os planos astrais. int também seria necessário.

Isto é, a menos que um novo tipo intrínseco fosse introduzido na linguagem comum.

JonHanna -- você quer dizer que esses três construtores:
operador implícito estático público uint (runa de runa);
operador implícito estático público Rune (char ch);
operador implícito estático público Rune (valor uint);

deve ser "int" em vez de "uint". AFAICT, int cobre facilmente todo o conjunto de planos astrais (não-BMP).

@PeterSmithRedmond Quero dizer que, assim como os dois construtores, um levando char e outro levando uint , deve haver um levando int , mas sim, também deve haver um int operador de conversão (apenas o que deveria ser implicit e o que explicit é outra questão). Não há mal nenhum em ter uint também para aquelas linguagens que podem usá-lo; afinal, é uma combinação bastante natural.

Se isso deve substituir System.Char deve ser possível fazer "aritmética" nele (ou seja ==, !=, >, < unsure on +, -, *, /) e, mais importante, deve haver suporte para literais deste digite, por exemplo, eu deveria ser capaz de escrever:

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


image

Se não rune , apenas outro sinônimo de character que poderia funcionar talvez seja letter ?

substantivo

  1. uma comunicação escrita ou impressa dirigida a uma pessoa ou organização e geralmente transmitida por correio.
  2. um símbolo ou caractere que é convencionalmente usado na escrita e impressão para representar um som de fala e que faz parte de um alfabeto.
  3. um tipo de impressão com tal símbolo ou caractere.

Embora isso entre em conflito com letra vs número

Letra tem um significado ainda mais preciso em unicode (e Net em geral) do que runa.

Acho que, se vamos fazer disso um tipo de caractere Unicode, precisamos seguir as convenções de nomenclatura do Unicode; que significa _"ponto de código"_.

Ponto de Código . (1) Qualquer valor no codespace Unicode; ou seja, o intervalo de inteiros de 0 a 10FFFF16. (Consulte a definição D10 na Seção 3.4, Caracteres e Codificação .) Nem todos os pontos de código são atribuídos a caracteres codificados. Consulte o tipo de ponto de código . (2) Um valor, ou posição, para um caractere, em qualquer conjunto de caracteres codificados.

Ou talvez desistamos e chamemos um pato de "pato" e nos refiramos a eles como caracteres Unicode (também conhecidos uchar ).

Por que não resolver isso para usar System.CodePoint ?
Imho é mais apropriado em termos de terminologia do Unicode, e outras pessoas no mundo Java estão usando. Então, em vez de termos o nosso próprio termo, vamos respeitar os termos Unicode. Faz mais sentido e mais universal em termos de caracteres gerais e implementação de string em .NET, sabendo também que String em .NET é uma coleção de char, e essa coleção de char é baseada em Unicode.

Eu sei, porque vivi em mundos Java e .NET.
E talvez vamos começar a ter um rascunho de implementação sobre isso.

Realmente existem dois componentes disso e ambos seriam necessários (CodeUnit em 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 são importantes para representar a codificação de largura variável e para uso em Span<ubyte> para garantir que as APIs de texto estejam disponíveis em tipos de texto, mas não em bytes brutos.

CodePoint / uchar é importante para um processamento sensato; por exemplo .IndexOf(❤) como ubyte por si só não pode ser usado para procurar um caractere unicode multibyte; e enumerar mais ubyte s seria perigoso, então o enumerador deve trabalhar em unidades uchar .

Combinando as duas propostas seria algo 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
    }
}

Eu tenho usado UnicodeScalar em minhas implementações de protótipo para se referir a um valor escalar Unicode (valores no intervalo U+0000..U+10FFFF, inclusive; excluindo pontos de código substitutos) e Utf8Char para se referir à unidade de código UTF-8. Parece que muitas pessoas preferem _Rune_ em vez de _UnicodeScalar_ porque é menos um bocado. Eu não me importo muito, mas vou salientar que o termo "Valor escalar Unicode" é o mesmo termo usado pela especificação Unicode . ;)

O .NET Framework também tem o conceito de "elemento de texto", que é um ou mais escalares que quando combinados criam um único grafema indivisível. Mais informações sobre isso no MSDN . Em particular, quando você enumera uma string, você pode querer enumerar por unidade de código ( Utf8Char ou Char ), valor escalar ( UnicodeScalar ), ou elemento de texto, dependendo do seu determinado cenário. Idealmente, daríamos suporte a todos os três tipos em String e Utf8String.

A superfície da API para nosso protótipo não está finalizada e está sujeita a mudanças rápidas, mas você pode ver algumas ideias atuais em https://github.com/dotnet/corefxlab/tree/utf8string/src/System.Text.Utf8/System /Text e https://github.com/dotnet/corefxlab/blob/master/src/System.Text.Primitives/System/Text/Encoders/Utf8Utility.cs.

Um pouco off-topic:
O "elemento de texto" deve ser a segmentação definida por "Grapheme Cluster Boundaries" em 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 atual:
👩‍⚕
🏻

👦
🏼
👨
🏽

👦
🏾

👦
🏿
👩‍⚕
🏼

👨
🏽

👦
🏼

👧
🏽
👩‍⚕
🏻

👩‍⚕
🏿

👧
🏼

👧
🏾

UnicodeScalar ainda é super fácil de digitar. u s c Espaço (preenchimento automático) Já que esse é o termo correto e mais autodescritivo, eu realmente espero que consigamos isso.

@ufcpp Esse é um bom ponto. Sinta-se à vontade para abrir um novo problema para isso. Se não pudermos alterar o comportamento por motivos de compatibilidade, sugiro que depreciemos esse tipo e criemos um enumerador de grafema compatível com especificações.

ubyte / uchar são confusos. Eles lêem como unsigned char / unsigned byte dada a convenção estabelecida com ushort / uint / ulong . Talvez char8 / u8char e char32 / u32char sejam mais claros?

De qualquer forma, acho que estamos desalinhados sobre se as unidades de código UTF-8 e os pontos de código são:

  1. tipos de dados primitivos de baixo nível em .NET - como byte , int
  2. um formato de dados para converter de/para primitivos existentes - como DateTime , Guid

E então, como expomos as APIs relacionadas ao codepoint dada essa decisão?

A opção 1 significa manipular texto por meio das primitivas char8, char16 e char32 (e acompanhar u8string, u16string e u32string) como C++17. Então char32 como rune é um nome ruim, dado que já temos char16 como char e precisamos de um terceiro nome para char8 também.

A opção 2 significa que byte e int/uint são 'bons o suficiente' para armazenar unidades de código UTF e pontos de código. Isso implica que todas as strings permanecem UTF-16. CodePoint / rune resolve problemas de semântica Code Point em vez de representação binária - e não se destina a IO .

IMO UTF-8/UTF-32 são apenas formatos de dados (opção 2). Trate-os como dados (byte/int). CodePoint é mais como DateTime ou Guid (outro identificador*) do que int para mim - não é um tipo primitivo de baixo nível, não suportado diretamente no IO (ou seja, BinaryWriter), não há necessidade de intrínsecos.

@miyu O protótipo que estamos apresentando no corefxlab está mais próximo da Opção 1. Existem tipos de dados específicos para representar unidades de código, e esses tipos de dados são para representação interna de dados textuais e não podem ser usados ​​para transmitir dados textuais pela rede. (Como você apontou, .NET já funciona assim hoje: System.Char é a unidade de código de uma string UTF-16, mas System.Char não pode ser enviado pela rede.)

Além disso, existem APIs para converter entre byte[] / Span<byte> / etc. (esta é a representação binária de todos os dados e é apropriada para E/S) e tipos primitivos como Utf8String / String / Guid / etc. Alguns deles são mais diretos do que outros. Por exemplo, podemos expor uma propriedade Utf8String.Bytes conveniência que retorna um ReadOnlySpan<byte> para uso em i/o, e esse getter de propriedade pode ter complexidade O(1). Não introduziríamos tal propriedade no tipo String , embora você possa imaginar ter um método de conveniência String.ToUtf8Bytes() . E mesmo que existisse uma propriedade Utf8String.Bytes , o tipo elementar de enumeração sobre uma instância Utf8String diretamente não seria byte . Seria Utf8CodeUnit (nome a ser definido) ou UnicodeScalar , o que acharmos que faz mais sentido para os tipos de aplicativos que os desenvolvedores desejam construir.

Idéia boba da parede - que tal wchar (_wide char_)? Hoje, a maioria dos ambientes de compilador C e C++ (fora do Windows) já usa wchar_t para representar o equivalente funcional de uma unidade de código de 32 bits. O Windows é uma exceção notável, onde wchar_t é definido como um tipo de 16 bits, mas os desenvolvedores que invocam o Windows hoje já precisam estar cientes das diferenças de largura de bits entre um .NET char e um estilo C char .

O tipo / palavra-chave wchar violaria nossas convenções de nomenclatura, mas apenas jogando isso para consideração.

Idéia boba da parede - que tal wchar (característica larga)?

Funciona para mim

O tipo / palavra-chave wchar violaria nossas convenções de nomenclatura, ...

Não parece que vamos obter uma palavra-chave curta da linguagem C#

https://github.com/dotnet/apireviews/pull/64#discussion_r196962756 parece extremamente improvável que introduzíssemos palavras-chave de linguagem para esses tipos, pois teriam que ser contextuais (ou seja, dependendo se podem resolver para um tipo com o nome da palavra-chave que eles ainda teriam que vincular a esse tipo, em vez do tipo representado pela palavra-chave).

Então, se queremos algo legal... ou seja NotLotsOfCapitalFullWords ...

Embora eu normalmente goste das convenções de nomenclatura do .NET, um nome longo é um pouco ofensivo para essencialmente um int que provavelmente também será usado em genéricos e como variáveis ​​de loop.

por exemplo, ninguém faz

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

Eles? (Certamente...)

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

é muito pior

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

Parece ok...

rune , wchar e uchar (sugeridos em outro tópico) soam bem para mim. Alguma sugestão para um par de string ? wstring , ustring , ou outro?

... e por que não obter uma palavra-chave da linguagem C#? Claro, não ter um para o primeiro lançamento faz sentido, mas se isso for no futuro, o manuseio de strings sem uma palavra-chave não é apenas falso, mas abertamente hostil em relação à sua adoção.

/CC @MadsTorgersen @jaredpar

por que não obter uma palavra-chave da linguagem C#?

Novas palavras-chave estão quebrando as alterações 100% das vezes. Não importa qual palavra você escolha, há uma empresa por aí que tem um tipo desse nome que é usado em todos os lugares em seu projeto. A única opção que temos são palavras-chave contextuais: var por exemplo.

Eu tenho sentimentos mistos sobre o uso de uma palavra-chave contextual para isso. As palavras-chave de tipo existentes ( int , string , etc ...) têm uma vantagem concreta sobre o nome do tipo real ( Int32 , String ):

  • string : refere-se ao tipo System.String no assembly que o compilador identifica como corelib. Este nome tem zero ambiguidade associado a ele.
  • String : o compilador não tem nenhum entendimento desse tipo. É apenas um tipo como qualquer outro e passa por todas as mesmas regras de pesquisa que os tipos que você define. Pode ser equivalente a string ou pode não ser.

Assim que introduzirmos palavras-chave contextuais aqui, rune poderá ser:

  • O tipo System.Rune dentro do assembly corelib
  • O tipo rune que você definiu dois anos atrás quando leu sobre Go .

A pesquisa de rune é tão ambígua quanto String , portanto, não vejo uma vantagem firme em tê-la como uma palavra-chave contextual.

BTW: é por isso que você deve usar string e não String 😄

BTW: é por isso que você deve usar string e não String

Qual 99% do motivo eu acho que as pessoas querem uma palavra-chave de idioma. O outro 1% sendo apenas "parece melhor" 😏

Polegares para baixo por forte aversão à palavra-chave "runa".

Uma palavra melhor é glifo, pois já representa o conceito geral de um símbolo elementar na tipografia.

Runa é um tipo específico de glifo que é ironicamente definido pelo Unicode. Referir-se a Go como arte anterior é um tanto ridículo. A arte anterior para runas é o que foi escrito em 150 dC e pedras rúnicas físicas reais. Não é o que alguém em Redmond pensa que é uma runa. Tentar redefinir conceitos existentes como esse é incomum, pois o .NET geralmente tem uma superfície de API bem projetada. Esta é uma rara exceção de nomenclatura de API muito ruim e quero expressar meu descontentamento.

Uma palavra melhor é glifo, pois já representa o conceito geral de um símbolo elementar na tipografia.

O problema é que "Glyph" é um termo usado ao renderizar o unicode para texto visível (de: utf8everywhere.org )

Glifo

Uma forma específica dentro de uma fonte. As fontes são coleções de glifos projetados por um designer de tipos. É responsabilidade do mecanismo de modelagem e renderização de texto converter uma sequência de pontos de código em uma sequência de glifos dentro da fonte especificada. As regras para essa conversão podem ser complicadas, dependentes da localidade e estão além do escopo do padrão Unicode.

Referir-se a Go como arte anterior é um tanto ridículo.

Usando o termo Rob Pike e Ken Thompson usado ao criar Utf-8 https://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt

Rob Pike trabalha em Go agora, e é por isso que usa o termo original.

Runa é um tipo específico de glifo que é ironicamente definido pelo Unicode.

Runic é definido por Unicode, Rune não é

Runic é definido por Unicode, Rune não é

Eu não acho que esta seja uma declaração precisa, a última especificação unicode (http://www.unicode.org/versions/Unicode11.0.0/UnicodeStandard-11.0.pdf) tem 37 hits para "rune" (apenas 36 são válidos , o último é parte de uma palavra maior) e é sempre usado para se referir a letras individuais do Alfabeto Rúnico.

Eu não acho que esta seja uma declaração precisa, a última especificação unicode tem 37 hits para "rune"

No corpo do texto descrevendo as motivações; não em nenhum nome de caractere ou nome de bloco de texto (onde seu caractere rúnico e rúnico)

No corpo do texto descrevendo as motivações; não em nenhum nome de caractere ou nome de bloco de texto (onde seu caractere rúnico e rúnico)

Ok, justo. Mas então voltamos à questão de que a especificação Unicode atual não define o termo "Rune" e quando é usado, é para texto informativo descrevendo "caracteres rúnicos".

O que formalmente define e usa para descrever as coisas é "Code Point" e "Code Unit".

  • Mesmo se, historicamente, o(s) criador(es) original(is) usaram o termo "Rune", a especificação oficial não (e eu imagino que eles tenham boas razões para não usá-lo).

Precisa ser curto ou seu uso fica feio

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

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

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

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

    return str.SubString(start, end);
}

vs

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

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

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

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

    return str.SubString(start, end);
}

Quanto ao comprimento, eu iria totalmente para CodePoint.IsWhiteSpace e str.GetCodePointAt , mas Rune também é divertido e eu não me importo com isso.

@jnm2 Não usaríamos GetCodePointAt quando se trata de strings. É muito ambíguo: não sabemos se você queria o char que estava nesse índice (já que todos os char s - mesmo substitutos não pareados - também são pontos de código válidos) ou o escalar / runa que estava nesse índice.

@GrabYourPitchforks GetRuneAt pode evitar o mesmo problema, ou você está dizendo que nenhum dos dois faria sentido?

@jnm2 Eu estava apenas dizendo que CodePoint em particular é muito ambíguo neste cenário. Caso contrário, o nome do método GetXyzAt deve corresponder ao nome do tipo Xyz que eventualmente entra.

Para sua informação, a implementação principal agora está com check-in (consulte https://github.com/dotnet/coreclr/pull/20935). Dê algum tempo para propagar para o corefx, então as APIs de referência virão via https://github.com/dotnet/corefx/pull/33395. Sinta-se à vontade para deixar este problema em aberto ou resolvê-lo como achar melhor.

Eu não espero influenciar ninguém ou ser capaz de mudar nada, mas apenas para o registro:

Uma palavra melhor é glifo, pois já representa o conceito geral de um símbolo elementar na tipografia.

O problema é que "Glyph" é um termo usado ao renderizar o unicode para texto visível (de: utf8everywhere.org )

Essa linha de raciocínio também não suporta runa, porque "runa" tem sido um termo usado por mais de mil anos ao longo da história, bem antes de Unicode ou transistores ou Microsoft ou código aberto existirem. Pelo menos indica que alguns arbitrariamente aplicam padrões diferentes a propostas diferentes, o que obviamente não é consistente, então talvez seja mais sobre quem foi o primeiro ou o mais alto do que o argumento mais coerente, o que eu sei. Eu sou apenas um atrasado tentando entender o processo, mas não faz sentido.

Referir-se a Go como arte anterior é um tanto ridículo.

Usando o termo Rob Pike e Ken Thompson usado ao criar Utf-8 https://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt

Rob Pike trabalha em Go agora, e é por isso que usa o termo original.

Go e Rob Pike são relativamente novatos neste tópico. Na verdade, a opinião deles é um tanto irrelevante em termos de definir o que é uma runa historicamente e na literatura e sociedade populares. Rob não martelou nenhuma pedra rúnica manualmente, então ele tem poucas qualificações para definir o que é uma runa. Aposto que ele não pode nem escrever ou ler scripts de runas, mas esse é o meu palpite. Na melhor das hipóteses, ele pode capturar esse conceito através da codificação, mas ele não pode entrar e dizer que um caractere chinês, escrita árabe ou Hangul ou carinha sorridente é uma runa ou qualquer outra coisa que seja um "Ponto de Código" agora também é uma Runa, ou algo assim. Parece quase atropelar o termo desrespeitosamente, veja, agora tudo pode ser uma runa, o que significa que as runas nada mais são do que um termo curinga de quatro letras para se referir a algo esotérico no domínio da codificação de texto.

Runa é um tipo específico de glifo que é ironicamente definido pelo Unicode.

Runic é definido por Unicode, Rune não é

O Unicode não deve redefinir o que é uma runa ou runa. Se o fizerem, estão a ultrapassar o seu mandato. Eles não têm nada que dizer ao público o que é uma runa. Na verdade, eles não têm nada a ver com a definição de qualquer nova linguagem ou sistema de caracteres. Eles não podem simplesmente se apropriar de uma palavra que já é um termo claramente sobrecarregado há mil anos e depois correr por aí torcendo como se tivessem inventado um novo conceito. A escrita rúnica consiste apenas em runas, e as runas já são um conceito estabelecido. Se você perguntar a uma pessoa aleatória em uma rua o que é uma runa, ela não pensará em Unicode.

Além de todos os problemas acima, a runa é uma metáfora pobre que é a pior parte. Não esclarece nada. Apenas adiciona outro nível de confusão. Qualquer recém-chegado ao tópico agora precisa passar por uma rodada de explicação e leitura de desambiguação, porque todos entram no contexto de que uma runa é um sistema de escrita histórico usado em certas culturas. A explicação terá que ser mais ou menos assim: "Uma runa é um ponto de código Unicode". "Mas por que não chamá-lo de ponto de código?" "Bem, porque é muito longo.", ou "Alguém decidiu que gosta de runa". Então, basicamente, porque alguém acha que 9 letras é muito em comparação com 4 (mesmo que eles tenham autocompletar com Intellisense e não seja nada comparado com o Java Kingdom Of Nouns), agora temos que lidar com essa confusão e explicar isso para milhares de desenvolvedores que podem precisar se interessar em Unicode. Basta usar uma instrução using para encurtar o termo se você usá-la muito no código.

Também não precisa ser UnicodeCodePoint, pode ser simplesmente CodePoint. Isso já é único. Existem muitos termos de API que são mais longos que "CodePoint", então isso deve ser suficiente. Se ainda for muito longo, use uma instrução using com alguma abreviação.

Prevejo que isso se torne uma daquelas perguntas pegadinhas de entrevista que realmente não agregam muito valor ou têm base lógica em nada útil. Pelo menos para a metáfora "marco", enquanto estamos no tópico de palavras simbólicas usadas no desenvolvimento de software baseado em conceitos derivados de pedra e rocha, um marco tem um significado descritivo real. Ele imediatamente comunica um conceito com o qual todos estão familiarizados. Ah, um marco, como quando você está em uma longa viagem e passa na trilha. É uma bela metáfora do mundo real que realmente ajuda a visualizar algo e pode se tornar uma linguagem gerencial instantaneamente. Não consigo imaginar as pessoas falando sobre runas dessa maneira, a menos que estejam intimamente familiarizadas com o tópico, e nesse ponto elas já saberão que é apenas um termo chamariz para ponto de código.

Uma palavra melhor é glifo, pois já representa o conceito geral de um símbolo elementar na tipografia.

O problema é que "Glyph" é um termo usado ao renderizar o unicode para texto visível (de: utf8everywhere.org)

Essa linha de raciocínio também não suporta runa, porque "runa" tem sido um termo usado por mais de mil anos ao longo da história, bem antes de Unicode ou transistores ou Microsoft ou código aberto existirem.

Meu ponto foi que a palavra "glifo" é problemática, pois já é usada como um dos conceitos na renderização de texto; é a representação gráfica desse caractere em uma fonte específica. Assim, um caractere pode ser representado por muitos glifos diferentes.

... novamente com @benaadams tendo a visão de 10.000 metros das coisas e a resposta correta 😁

Honestamente, vamos ter que viver com o velho ditado: "você pode fazer algumas pessoas felizes o tempo todo, e todas as pessoas felizes algumas vezes; mas você não pode fazer todas as pessoas felizes o tempo todo. A Hora." Esta é uma situação do primeiro.

Sigilo?

Exit, pursued by a bear.

Como alguém que usaria essa API extensivamente, estou votando fortemente no code point. A terminologia Unicode já é bastante confusa e as inconsistências já são abundantes. Você tornará minha vida muito mais fácil se eu puder dizer “ponto de código” em todos os lugares.

Estou deitada na cama agora. Se eu virar de lado, enfrento um quadro branco encostado na parede. Durante meses, esse quadro branco foi o lar de vários rabiscos e gráficos enquanto tento descobrir como lidar com IDNs de forma eficiente em C#. Eu a trato como uma relíquia que convoquei das profundezas do inferno. Se eu tentasse explicar a lógica que ela descreve, não seria capaz.

Por favor, não torne minha vida mais difícil. Um ponto de código é um ponto de código. Não é uma runa, glifo, caractere, grafema ou mesmo símbolo. Ele não precisa representar nada significativo para um humano – pode ser um código de controle. Pode não representar um símbolo visual, como o nome “runa” implica. É apenas um ponto de código.

Um argumento mais concreto é que “runa” implica a representação de um único grafema, o que muitas vezes não é o caso. Se eu contar o número de pontos de código e o número de grafemas, posso obter dois números muito diferentes. A mesma sequência de grafemas pode ser representada por duas séries distintas de pontos de código.

Uma palavra melhor é glifo, pois já representa o conceito geral de um símbolo elementar na tipografia.

Isso é ainda pior. Um único ponto de código pode ser representado por vários glifos e um único glifo pode representar vários pontos de código. O mapeamento exato pode variar de acordo com o sistema, programa, tipo de letra...

Todas essas palavras têm significados técnicos muito específicos. Embora as diferenças possam parecer insignificantes no contexto desta proposta, elas têm consequências reais em outros lugares, especialmente em outros idiomas além do inglês.

Apenas como um exemplo de como pode ser difícil lidar com texto, mesmo em um idioma tão comum quanto o alemão:

  1. Converta ß para maiúscula e você terá SS .
  2. Converta de volta para minúscula e você obterá ss .

Problemas:

  • O que char.ToUpper('ß') deve retornar? (Tem que retornar um único caractere.)
  • Uma versão maiúscula de ß que meu telefone não pode inserir nesta caixa de texto foi adicionada ao Unicode 5.1. Se eu tentar colá-lo, recebo SS. Agora, as conversões superiores/inferiores são ainda mais ambíguas.
  • Alterar o revestimento de uma string altera seu comprimento.
  • As alterações de caso não são idempotentes ou reversíveis.
  • Você não pode realizar uma comparação que não diferencia maiúsculas de minúsculas simplesmente colocando cada string em minúsculas.

Embora este não seja um exemplo direto de uma situação em que a terminologia causa problemas, ele demonstra como existem tipos de casos extremos nos quais normalmente não pensamos. Dar a cada termo um significado distinto e consistente ajuda os programadores a comunicar essas questões. Se eu pedir a um colega de equipe para escrever uma função para contar grafemas, ele saberá exatamente o que vai contar e como fazê-lo. Se eu pedir para contar pontos de código, novamente, eles sabem exatamente o que fazer. Essas definições são independentes das linguagens e tecnologias que estamos usando.

Se eu pedir a um desenvolvedor de JavaScript para contar runas, eles vão olhar para mim como se eu tivesse três cabeças.

Wikipedia diz

Unicode define um espaço de código de 1.114.112 pontos de código no intervalo de 0hex a 10FFFFhex

Code point parece ser o nome oficial. Eu li este tópico e não encontrei um argumento forçado para o motivo do ponto de código estar incorreto.

Concordo que ponto de código não é o termo correto para usar aqui. No mínimo, com base no padrão Unicode, não inclui valores acima de 10FFFF (http://unicode.org/glossary/#code_point).

Talvez essa frase esteja errada? Ele diz "qualquer valor no espaço de código". Portanto, isso claramente significa tudo e, ao mesmo tempo, obtém o número inteiro errado.

Além disso, "rune" tem um significado no mundo real que não tem nada a ver com Unicode. Na Alemanha, a palavra "Runa" tem conotações nazistas porque as runas têm uma história "germânica" à qual os nazistas gostavam de se referir.

Acho "rune" um nome confuso. Alguém aqui realmente gosta de "rune" ou os argumentos para isso são baseados na correção. Intuitivamente, é um nome muito ruim.

Talvez essa frase esteja errada? Ele diz "qualquer valor no espaço de código". Portanto, isso claramente significa tudo e, ao mesmo tempo, obtém o número inteiro errado.

Essa frase está correta. O espaço de código é de U+0000 a U+10FFFF. O Unicode teoricamente poderia ser expandido além disso algum dia, mas quebraria o UTF-8 e o UTF-16. Precisaríamos de novas codificações.

Edit: Na verdade, não me cite sobre a quebra do UTF-16, mas tenho certeza de que quebraria o UTF-8. UTF-8 definitivamente não pode representar 0xFFFFFF (2^24 -1).

Editar 2: Para esclarecer, o Unicode afirma que os pontos de código nunca podem exceder U + 10FFFF. Isso não significa que há atualmente 0x110000 pontos de código - a maioria desses pontos de código não está atribuída.

@Zenexer @GSPP

Este tipo como atualmente registrado no master ( System.Text.Rune ) mapeia muito especificamente para um "valor escalar Unicode" ( consulte o glossário ). Os ctors do tipo lançarão uma exceção se você tentar construí-lo a partir dos valores -1 , 0xD800 ou 0x110000 , já que esses não são valores escalares pela especificação Unicode. Se você usar um parâmetro Rune como entrada para seu método, não precisará realizar nenhuma verificação de validação nele. O sistema de tipos já garantiu que foi construído a partir de um valor escalar válido.

Re: conversão de maiúsculas e minúsculas, todas as APIs de conversão de maiúsculas e minúsculas no .NET Framework _salvo indicação em contrário_ usam uma técnica chamada dobragem simples de maiúsculas e minúsculas. De acordo com as regras para dobra simples de maiúsculas e minúsculas, para qualquer valor escalar de entrada, os formulários de saída em minúsculas, maiúsculas e maiúsculas também são garantidos para serem exatamente um valor escalar. (Algumas entradas, como os dígitos 0-9 ou símbolos de pontuação, não têm entradas no mapa de conversão de maiúsculas. Nesses casos, operações como _ToUpper_ simplesmente retornam o valor escalar de entrada.) no Plano Multilíngue Básico (BMP), então a saída também deve estar no BMP; e se a entrada está em um plano suplementar, a saída também deve estar em um plano suplementar.

Existem algumas consequências para isso. Primeiro, Rune.ToUpper e amigos sempre retornarão um único valor _Rune_ (escalar). Em segundo lugar, String.ToUpper e amigos sempre retornarão uma string com exatamente o mesmo comprimento de sua entrada. Isso significa que uma string contendo 'ß' (eszett minúsculo), após uma operação de conversão de maiúsculas e minúsculas, pode acabar contendo 'ß' (sem alteração) ou 'ẞ' (eszett minúsculo), dependendo da cultura que está sendo usada. Mas _não_ conterá "SS", porque isso alteraria o comprimento da string, e quase todas as APIs de conversão de maiúsculas e minúsculas .NET expostas publicamente usam regras simples de dobra de maiúsculas e minúsculas. Terceiro, Utf8String.ToUpper e amigos (ainda não registrados) são _não_ garantidos para retornar um valor cuja propriedade _Length_ corresponda à propriedade _Length_ do valor de entrada. (O número de unidades de código UTF-16 em uma string não pode ser alterado após a dobra simples de maiúsculas, mas o número de unidades de código UTF-8 em uma string pode mudar. Isso ocorre devido à forma como os valores BMP são codificados por UTF-16 e UTF- 8.)

Existem algumas APIs .NET que usam internamente regras complexas de dobra de maiúsculas e minúsculas em vez de regras simples de dobra de maiúsculas e minúsculas. String.Equals , String.IndexOf , String.Contains e operações semelhantes usam regras complexas de dobra de maiúsculas e minúsculas nos bastidores, dependendo da cultura. Portanto, se sua cultura estiver definida como _de-DE_, a string de um caractere "ß" e a string de dois caracteres "SS" serão comparadas como iguais se você passar _CurrentCultureIgnoreCase_.

@GrabYourPitchforks Estou me opondo principalmente à escolha do nome. O exemplo de casefolding foi puramente para enfatizar o quão complicado o Unicode (e o texto em geral) pode ser. Contanto que haja alguma maneira de lidar com a normalização , não me importo muito com o funcionamento das operações simples, pois estarei convertendo para NFKD para tudo de qualquer maneira para o meu caso de uso.

Essa frase está correta. O espaço de código é de U+0000 a U+10FFFF. O Unicode teoricamente poderia ser expandido além disso algum dia, mas quebraria o UTF-8 e o UTF-16. Precisaríamos de novas codificações.

Apenas para ser nitpicking (ou, se as pessoas estiverem interessadas): Em teoria, o algoritmo UTF-8 funciona para até 42 bits (Byte de prefixo 0xFF e 7 bytes de carga útil de 6 bits), e originalmente, as primeiras especificações cobriam os 31 bits completos espaço de bits dessas versões antigas do Universal Character Set (UCS4) - no entanto, as especificações atuais (RFC 3629, Unicode Standard, Anexo D da ISO/IEC 10646) concordam em restringi-lo ao intervalo atual de codepoints válidos (U+ 0000 a U+10FFFF).

Para UTF-16, a situação é mais difícil. Mas eles poderiam reservar pontos de código em um plano superior como "Escapes" para 32 bits ou mais. Como os aviões 3 a 13 estão atualmente indefinidos, eles poderiam reservar dois deles como "plano substituto baixo" e "plano substituto alto". Em seguida, um codepoint de 32 bits seria dividido em dois valores de 16 bits (um em cada plano) e, em seguida, cada valor seria codificado usando dois substitutos "clássicos", efetivamente usando 4 unidades de código de 16 bits cada para codificar um codepoint de 32 bits.

Aliás, AFAICS, o consórcio unicode declarou publicamente que eles nunca alocarão codepoints acima de U + 10FFFF, então, na prática, espero estar aposentado antes que isso realmente aconteça. :piscadela:

Este tipo como atualmente registrado no mestre ( System.Text.Rune ) mapeia muito especificamente para um "valor escalar Unicode"

@GrabYourPitchforks obrigado por esse esclarecimento. Isso significa que a estrutura não representa um ponto de código. Então, esse nome de fato seria incorreto.

Acho que UnicodeScalar é muito misterioso como um nome...

@GrabYourPitchforks , o que resta fazer para este problema?

@stephentoub Não há nenhuma funcionalidade adicional planejada para o tipo Rune in-box para 3.0, mas @migueldeicaza teve ideias para estender o alcance do tipo, inclusive para coisas como clusters de grafema. (A coisa mais próxima que temos na caixa é TextElementEnumerator , que é um tipo muito desatualizado.) Algumas dessas idéias foram cogitadas neste tópico, mas ainda não há nada concreto.

Poderíamos deixar este problema em aberto caso a comunidade queira discutir mais os cenários, ou podemos direcionar as pessoas para abrir novos problemas se quiserem fazer sugestões específicas. TBH Eu não tenho uma forte preferência.

Obrigado. Como o Rune já foi introduzido e as APIs descritas aqui (ou aproximações) já expostas, vamos encerrar isso. Suporte adicional pode ser abordado por meio de problemas separados.

Então, isso está essencialmente estabilizado neste momento? Porque, com toda a honestidade, esse nome terrível, que não se alinha com nenhuma informação que você encontrará sobre Unicode de fontes boas e precisas, e tem a infeliz nuance de implicar um glifo em vez de um caractere não imprimível, só vai piorar a já terrível compreensão do Unicode por seu programador médio.

Eu sei que isso foi integrado por este ponto, mas eu só quero falar sobre a parte Rune e algumas pessoas discordam sobre o nome.

Eu encontrei Rune pela primeira vez no Plano 9, e como outros já viram em Go e outros. Quando os msdocs começaram a listar Rune eu sabia exatamente o que era antes de ler.

Em pelo menos duas instâncias, Plan 9 e Go, você tem os indivíduos responsáveis ​​pelo UTF-8 usando o nome Rune . Eu acho que é seguro dizer que eles já pensaram sobre essas preocupações e ainda achavam que Rune era razoável. Runic não é mais um sistema de escrita usado, exceto com alguns tradicionalistas. E Rune significa o grafema nesse sistema, assim como significa essencialmente o grafema aqui (exceto em casos como caracteres de controle.

Eu realmente vejo pouco errado com a nomenclatura. Runic é um sistema de escrita tão antigo que duvido muito que seu programador médio vá confundi-lo, e já existe um padrão de fato de várias décadas de Rune para "caracteres" Unicode adequados.

@Entomy

assim como significa essencialmente o grafema aqui (exceto em casos como caracteres de controle.

Isto simplesmente não é verdade. O Unicode contém um grande número de pontos de código pré-compostos que representam vários grafemas (geralmente combinações de letras e diacríticos), e são comumente usados ​​para escrever idiomas como francês e espanhol, e praticamente todo o texto computadorizado nesses idiomas usará esses códigos pontos.

Por outro lado, mesmo quando um único ponto de código representa um grafema, é muito comum que eles se combinem em um _grupo de grafemas_, o que é essencial para o manuseio adequado do texto na maioria dos idiomas indianos. Assim, um único caractere percebido pelo usuário ao se mover com as teclas de seta geralmente corresponde a vários pontos de código em sequência. Portanto, não pode haver correspondência fácil entre pontos de código e grafemas ou agrupamentos de grafemas. Até mesmo "personagem" provavelmente seria um nome melhor, considerando que os programadores estão acostumados a considerar caracteres estranhos e malucos neste momento, enquanto "runa" dá a impressão de que a questão de descobrir os limites de caracteres percebidos pelo usuário foi resolvida para o programador já quando de fato não foi.

Quando os msdocs começaram a listar Rune, eu sabia exatamente o que era antes de ler.

O fato de você pensar que o nome runa descrevia bem os grafemas é uma evidência muito boa do problema que tenho aqui: o nome “runa” dá aos programadores uma falsa sensação de segurança, tornando mais fácil supor que existe tal correspondência.

Em pelo menos duas instâncias, Plan 9 e Go, você tem os indivíduos responsáveis ​​pelo UTF-8 usando o nome Rune .

Por mais respeito que eu tenha por Ken Thompson e Rob Pike, o trabalho deles aqui foi essencialmente apenas conceber um esquema muito inteligente para codificar uma série de inteiros de comprimento variável. Eles não são especialistas em Unicode como um todo, e eu discordo deles muito fortemente nesta questão. Admito que também não sou especialista em Unicode, mas não acho que o apelo à autoridade aqui seja tão forte quanto possa parecer.

e já existe um padrão de fato de várias décadas do Rune para "caracteres" Unicode adequados.

"Padrão" você diz? Tem sido principalmente esses dois empurrando o nome, e algumas linguagens de programação menores, como Nim, adotando-o de Go. E é claro que devo repetir novamente que um ponto de código não representa um único “caractere Unicode adequado”, seja no sentido de seleção, movimento da tecla de seta, grafemas ou agrupamentos de grafemas.

... essencialmente significa o grafema aqui ...

Sim, pois não exatamente, mas aproximadamente perto o suficiente. Grafemas, pelo menos como são definidos em linguística, são os componentes ortográficos que compõem um sistema de escrita e são usados ​​para expressar fonemas. Estes não são uma coisa 1:1. Em silabários e logossilabários, um único grafema pode representar vários fonemas, normalmente um par consoante-vogal. Por outro lado, as línguas alfabéticas geralmente têm casos de vários grafemas representando um único fonema, como "th" em inglês sendo responsável pelo arcaico eth e thorn, dependendo da palavra específica. Então você não pode nem mesmo encontrar acordo entre os idiomas sobre se uma letra como 'á' é sua própria letra única, ou 'a' com acento. Não podemos nem mesmo estabelecer consistência em idiomas com milhares de anos. Não teremos uma adição perfeitamente consistente em cima disso, que é a codificação deles.

Como você está defendendo uma semântica extremamente estrita, o que o UNICODE chama de "grupo de grafemas" geralmente é em linguística apenas um único grafema. Isso invalida o UNICODE? Não. Isso significa que o UNICODE precisa renomeá-lo? Não por que? Porque contexto. Os campos têm sua própria linguagem e, desde que não haja confusão dentro de um único campo, isso não é um problema.

Não vejo o nome como um grande negócio. Msdocs é claro sobre o que Rune está no resumo. Se as pessoas não lerem os documentos, o problema é delas. As pessoas não estão reagindo com veemência ao 'Stream' e dizendo bobagens como "ah, mas e se as pessoas acharem que é um rio pequeno, porque já tem o mesmo nome!" Não.

@Serentty @Entomy Vocês dois também podem estar interessados ​​na classe StringInfo , que expõe o conceito real de Unicode "clusters de grafema estendidos". O tipo StringInfo é bastante antigo e, como resultado, implementa uma versão muito antiga do padrão Unicode, mas há um trabalho ativo para atualizá-lo para ser compatível com UAX #29, Sec.

Sim, pois não exatamente, mas aproximadamente perto o suficiente.

Acho que a questão das representações compostas versus decompostas torna isso falso. Se estamos indo pela definição linguística de um grafema aqui em oposição a qualquer tipo de definição relacionada à computação, então 한 e 한 são exatamente a mesma sequência de grafemas (três Hangul jamo representando a sílaba _han_ como os segmentos HAN), e no entanto, o primeiro é apenas um ponto de código, enquanto o segundo é uma sequência de três.

Os campos têm sua própria linguagem e, desde que não haja confusão dentro de um único campo, isso não é um problema.

Este é exatamente o meu ponto também. O Unicode é um sistema realmente complicado com sua própria terminologia, então por que tentar forçar algum tipo de termo “intuitivo” mal elaborado quando ele não se alinha com tanta precisão? Pontos de código são pontos de código. Eles não têm paralelo linguístico, e tentar ser intuitivo enquanto apenas 75% de precisão é uma receita para o mesmo tipo de desastre do qual o C# ainda está tentando se recuperar.

Como você está defendendo uma semântica extremamente estrita, o que o UNICODE chama de "grupo de grafemas" geralmente é em linguística apenas um único grafema.

No padrão, um cluster pode incluir apenas um único grafema. Não há nada de errado com isso aqui. Um _cluster_ é uma unidade de seleção de texto e movimento do cursor.

Não vejo o nome como um grande negócio. Msdocs é claro sobre o que é Rune no resumo. Se as pessoas não lerem os documentos, o problema é delas.

Este é o argumento “programadores precisam ser mais inteligentes” que surge repetidamente em defesa de más decisões de design. Se os programadores precisam ler a documentação e aprender que uma runa é um ponto de código Unicode, então qual é o sentido de chamá-la de um nome mais “intuitivo” em primeiro lugar? O argumento aqui parece ser que “ponto de código” é confuso, então faz sentido escolher um nome mais intuitivo, mas quando enfrentamos a questão do nome ser enganoso, a defesa é que os programadores deveriam saber o que é um ponto de código de qualquer maneira. da leitura da documentação. Se for esse o caso, por que não apenas chamar o tipo CodePoint e facilitar para os programadores procurarem e aprenderem? Isso tudo deixa de lado o problema de que a documentação do .NET é bastante terrível com relação ao Unicode em primeiro lugar, trata os pares substitutos como uma reflexão tardia em um mundo de “caracteres Unicode de 16 bits”.

Este é o argumento “programadores precisam ser mais inteligentes” que surge repetidamente em defesa de más decisões de design.

Eu nunca disse isso.

O argumento aqui parece ser que “ponto de código” é confuso

Eu também nunca disse isso.

As pessoas não estão reagindo com veemência ao 'Stream' e dizendo bobagens como "ah, mas e se as pessoas acharem que é um rio pequeno, porque já tem o mesmo nome!" Não.

Estou dizendo que os programadores são espertos o suficiente para não pensar que Rune é especificamente uma runa rúnica, da mesma forma que eles sabem que Stream não é um rio pequeno.

Deixe-me repetir isso

Estou dizendo que os programadores são inteligentes o suficiente para descobrir isso. Você está colocando palavras na minha boca.

Não vejo o nome como um grande negócio. Msdocs é claro sobre o que é Rune no resumo. Se as pessoas não lerem os documentos, o problema é delas.

É a isso que me refiro aqui. O argumento a favor do nome “runa” é baseado na intuição e na conexão intuitiva com a noção de grafema. Você mesmo estava argumentando que os dois estavam alinhados o suficiente para que não fosse um problema. Quando apontei todas as maneiras pelas quais essa intuição estava errada e a correspondência poderia ser muito ruim, sua resposta foi essencialmente que não importava porque os programadores precisavam ler a documentação de qualquer maneira. Isso é o que quero dizer com “programadores precisam ser mais inteligentes”. A documentação não é uma desculpa para nomes enganosos quando não há motivo herdado para eles.

Estou dizendo que os programadores são espertos o suficiente para não pensar que Rune é especificamente uma runa rúnica, da mesma forma que eles sabem que Stream não é um rio pequeno.

Meu argumento aqui não é que as pessoas vão confundi-lo com runas rúnicas. Meu argumento é que as pessoas vão confundi-lo com glifos, grafemas e agrupamentos de grafemas, que, apesar de sua insistência, se correlacionam muito mal com pontos de código.

Estou dizendo que os programadores são inteligentes o suficiente para descobrir isso. Você está colocando palavras na minha boca.

Inteligente o suficiente para descobrir que não são runas germânicas reais, com certeza. Mas descobrir que eles não são glifos, grafemas ou agrupamentos de grafemas? Minha experiência real com a qualidade do manuseio de Unicode pela maioria dos softwares diz que não.

Se as pessoas não lerem os documentos, o problema é delas.

Sim, e eu mantenho isso. Não por uma questão de deficiência de inteligência, mas de tendência a suposições precipitadas.

Se um programador assume que String significa um pedaço de corda forte e fino, feito a partir da torção de fios, porque, sim, isso significa que isso não é considerado um problema com o nome String .

Se um programador assumir que Char significa um material carbonizado, como carvão, ou um tipo específico de truta, isso não é considerado um problema com o nome Char .

Se um programador assume que character significa a representação de um conjunto de traços mentais e éticos usados ​​na narrativa, isso não é considerado um problema com o nome character .

Observe que estes são todos assuntos textuais/linguísticos. Todos eles têm outros significados. E, no entanto, os programadores se acostumaram muito bem. Esses termos tornaram-se padrões de fato, por causa de uma convenção estabelecida no campo: nossa linguagem. Há um precedente estabelecido de que os programadores são espertos o suficiente para acompanhar isso.

Você mesmo estava argumentando que os dois estavam alinhados o suficiente para que não fosse um problema.

Sim, este é o GitHub. Em uma questão já encerrada, onde eu estava apenas acrescentando meus pensamentos sobre por que eu achava que Rune estava bem porque havia algum precedente estabelecido no nome. Este não é o lugar nem o contexto para escrever um tratado, repleto de definições extensas e palavras cuidadosamente escolhidas. Por exemplo, se estou colocando um PR para, digamos, um decodificador UTF-8, não vou descrever explicitamente por que implementei o DFA Hoehrmann sobre abordagens alternativas. Eu só vou dizer "aqui está, aqui está uma prova de que funciona, aqui estão alguns benchmarks que comprovam por que eu fui com isso".

Meu argumento é que as pessoas vão confundi-lo com glifos, grafemas e agrupamentos de grafemas

Eles não estão confundindo nenhum dos itens mencionados acima, nem Tree , Heap , Table , Key , Socket , Port ...

Este é um argumento extremamente falso. Um pedaço de fio e uma sequência de texto não são facilmente confundidos. Uma planta alta e uma estrutura de dados de árvore não são facilmente confundidas. Um ponto de código, por outro lado, é um conceito muito mal compreendido pela maioria dos programadores e constantemente confundido com todos os outros conceitos que discutimos. A solução para isso é, como você diz, ler a documentação. No entanto, uma linguagem que usa seu próprio nome “inteligente” para pontos de código torna ainda mais difícil aplicar o conhecimento da _documentação real do Unicode_ a essa linguagem. E isso me leva a isso:

Esses termos tornaram-se padrões de fato, por causa de uma convenção estabelecida no campo: nossa linguagem.

E este é o cerne de tudo. Você parece estar afirmando que “rune” é um termo bem estabelecido para um ponto de código que é amplamente compreendido na programação, ou deveria ser. Se for o primeiro, convido você a perguntar a um programador médio experiente em uma linguagem de programação importante que não seja Go se ele já ouviu isso. Se for o último, eu gostaria de perguntar qual é o objetivo de competir com a terminologia oficial do Unicode em uma situação já confusa e mal compreendida que é frequentemente mal compreendida até mesmo por desenvolvedores altamente experientes.

@Entomy entrada de fora: todo o seu argumento, até onde posso dizer, é 'é confuso e ruim, sim, mas não é tão confuso e ruim'.
Assim? Por que não pode ser realmente bom em vez disso? Qual é o problema em nomeá-lo exatamente como Unicode o nomeia?
Além disso, as runas não são pontos de código, nem mesmo grafemas ou clusters, no campo geral da computação. Se você pesquisar 'Runas Unicode' no Google, qualquer coisa relacionada a pontos de código não aparece até a página 2, e mesmo assim são apenas links godoc / Nim. Mesmo no DuckDuckGo, com o qual os programadores podem se sentir mais confortáveis, ainda é um resultado de página 2. Portanto, o único argumento que resta para o nome que vi é que é intuitivo que ele represente um ponto de código, mas não é . É intuitivo que represente um cluster de grafemas, ou talvez apenas um grafema.
Fonte: Eu usei Go e pensei que era um grafema até quatro anos depois, quando li esta edição agora há pouco.

(e dizer que não há problema em sugerir um grafema porque é 'perto o suficiente' me lembra do caractere de 16 bits estar próximo o suficiente.)
Sim, se os programadores fossem mais inteligentes e lessem mais documentação, não precisaríamos de um nome significativo para isso, ou mesmo de um tipo. As pessoas saberiam apenas passar pontos de código em um int em vez de char. Mas eles não são. Eles são tão inteligentes quanto são agora, e isso não vai mudar só porque a Yet Another API foi adicionada. O objetivo é aumentar a quantidade de software que lida corretamente com outros idiomas além do inglês, não apenas introduzir novas maneiras de fazer a mesma coisa e manter as mesmas barreiras de entrada de antes.

Apenas por uma questão de argumento, e para fins científicos, eu gostaria de apontar a todos aqui a linguagem de programação que faz melhor manipulação de texto Unicode, onde »melhor« é definido por »mais próximo de acordo com o padrão Unicode«, não fingindo simplicidade: Swift

  • String é um buffer de texto Unicode arbitrário.
  • Character , sobre o qual você itera e quais não, não é um único valor escalar Unicode, mas um cluster de grafema estendido. Veja este exemplo para o cluster de grafemas : let decomposed: Character = "\u{1112}\u{1161}\u{11AB}" // ᄒ, ᅡ, ᆫ
  • Se você precisar de valores escalares Unicode, também poderá iterar sobre eles. Seu tipo é chamado UnicodeScalar .
  • E se você realmente sentir que precisa, você também pode iterar sobre unidades de código UTF-8 e UTF-16, gerando UInt 8 se UInt 16 s.

Agora, não estou aqui sugerindo que o C# siga o estilo Swift completo. Embora isso seja incrível, também é necessário um monte de mudanças e trabalho. Estou aqui para sugerir a escolha da nomenclatura no estilo Swift, no entanto, por todas as razões apontadas pelo @Serentty , e para deixar a opção aberta para transformar as strings de texto no estilo Swift eventualmente.

Alguns nomes potenciais melhores que Rune : CodeUnit32 , UnicodeScalar , CodeUnit , UniScalar , UnicodeValue , UniValue , UnicodeScalarValue . Acho que os dois primeiros podem se encaixar perfeitamente nas convenções de nomenclatura do C#. Observe que UnicodeScalar é objetivamente o melhor nome, pois as unidades de código são apenas maneiras de codificar um valor escalar Unicode na linguagem Unicode. Portanto CodeUnit32 implica iterar sobre as unidades de código de uma string de texto codificada em UTF-32, enquanto UnicodeScalar é independente de codificação.

Edit: Sim, o nome System.Rune já existe. Tudo isso é apenas um »se queremos melhorar antes que essa coisa tenha meia década«.

@pie-flavor

todo o seu argumento, até onde posso dizer, é 'é confuso e ruim, sim, mas não é tão confuso e ruim'.

Não, esse não é o meu argumento. Estou fazendo o melhor com a deficiência que tenho, mas esta não é a minha comunicação pretendida.

Se você pesquisar 'Runas Unicode' no Google, qualquer coisa relacionada a pontos de código não aparece até a página 2, e mesmo assim são apenas links godoc / Nim.

Se você pesquisar 'string Unicode' no Google, também não obterá especificamente como as strings .NET funcionam. Esta é uma questão de procurar uma coisa adjacente. Como uma analogia muito estrita, programo tanto em .NET quanto em Ada; string não é o mesmo entre eles, e uma pequena leitura para cada um é uma boa ideia.

Definições sobrecarregadas não são incomuns na linguagem, e ainda assim conseguimos muito bem. Pode surpreendê-lo, mas "run" tem pelo menos 179 definições formais, "take" tem pelo menos 127, "break" tem pelo menos "123" e assim por diante. [ fonte ] As pessoas são incrivelmente capazes e podem navegar com sucesso por muito mais complexidade do que o que é considerado problemático aqui. A preocupação de "rune" ter pelo menos 2 definições formais, na minha opinião, não é justificada quando as pessoas podem lidar com mais de 50x as sobrecargas.

Além disso, isso está explorando grosseiramente o comportamento do mecanismo de pesquisa. Com a maioria dos mecanismos de pesquisa, você obtém resultados com base em quantas páginas estão vinculadas a algo. Existem outros fatores também, com cada abordagem pesando as coisas de maneira diferente. Como o .NET Rune é um conceito bastante recente em comparação, haverá muito menos conteúdo falando sobre ele e serão necessárias mais páginas para chegar até ele. Mas também está usando a ferramenta de pesquisa errada. Se eu quiser encontrar pesquisas sobre algoritmos de busca de strings, para ver se algo novo surgiu nos últimos anos, não procuro no Google ou no DDG. Semantic Scholar, Google Scholar e outros são melhores pontos de partida. Da mesma forma, se você quiser entender as coisas sobre as APIs .NET, pesquise primeiro os MSDocs. Se eu reclamar que "momento de inércia", um termo de física/engenharia, é vago ou enganoso em seu nome, e deve ser renomeado porque não consigo encontrar nenhuma informação sobre ele nos primeiros livros, começando pelo número mais baixo em uma biblioteca usando a Classificação Decimal de Dewey, isso não é um problema com a nomenclatura de "momento de inércia"; Estou claramente procurando no lugar errado.

Fonte: Eu usei Go e pensei que era um grafema até quatro anos depois, quando li esta edição agora há pouco.

Dei uma olhada nos documentos do Go e nas notas de lançamento, pelo menos os que consegui encontrar, e tenho que concordar com você. Eles são muito vagos sobre o que rune é e, infelizmente, são até vagos sobre o tamanho rune . Suspeito que essa imprecisão causará problemas mais tarde, pois vi Ada ser igualmente vaga sobre as restrições de tipo de dados e tê-lo mordido na bunda anos depois.

No entanto, devo dizer que o msdocs faz um trabalho muito melhor com uma descrição muito detalhada e concisa.

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

Dito isto, as observações estão um pouco ausentes e alguma elaboração sobre por que Rune existe e quando você gostaria de usá-lo seria benéfico (e também o local apropriado para uma explicação mais detalhada do que a minha simplificada acima mencionada) . Vou apresentar algumas melhorias lá.

@Evrey

Apenas para fins de argumentação e para fins científicos, gostaria de apontar a todos aqui a única linguagem de programação que melhor lida com o texto Unicode

Esta é uma opinião. Um com o qual concordo absolutamente; O Swift certamente lida melhor com o UNICODE moderno. Mas sem uma citação de pesquisas reproduzíveis revisadas por pares confirmando esses resultados, isso não é uma afirmação científica.

Agora, não estou aqui sugerindo que o C# siga o estilo Swift completo. Embora isso seja incrível, também é necessário um monte de mudanças e trabalho.

E quebraria o software existente.

deixe a opção aberta para transformar as strings de texto no estilo Swift eventualmente.

E quebraria o software existente.

Sim, o nome System.Rune já existe. Tudo isso é apenas um »se queremos melhorar antes que essa coisa tenha meia década«.

E quebraria o software existente.

Como hipotético, se alterações fossem feitas no nome existente, como você propõe software existente direcionado ao .NET Core 3.0/3.1, onde Rune já está em uso, ainda seja compatível, embora também exista como um nome diferente em tempos de execução de destino posteriores?

E quebraria o software existente.

Como mencionado, estou apenas argumentando da perspectiva do princípio e do idealismo. A realidade das coisas foi amplamente mencionada. Embora haja alguma nuance em tudo isso:

  • Ir ao estilo Swift com strings não necessariamente quebra o software. É apenas uma questão de adicionar mais métodos e tipos de enumeração sobre a interface String já existente. Eu não quero dizer coisas radicais como mudar System.Char em um tipo de cluster de grafema ou algo assim.
  • Se um nome de tipo existente como System.Char fosse reaproveitado para um tipo diferente, então sim, isso seria uma grande mudança. E uma mudança irresponsável nisso. Estou com você lá.
  • Um hipotético .NET Core 4.0, falando em SemVer, pode fazer o que quiser. Fora isso, as mudanças até um hipotético 4.0 não são tão assustadoras: Transforme System.Rune em um alias de tipo obsoleto para System.UnicodeScalar ou qualquer que seja o nome. O software que usa Rune não notará a diferença, exceto uma nota de descontinuação, e um novo software pode usar o tipo real com melhor nome. E um hipotético 4.0 então apenas cai Rune .
  • Da mesma forma, System.Char pode ser transformado em um alias para System.CodeUnit16 ou algo assim.
  • Fazer isso no estilo Swift significa apenas adicionar System.GraphemeCluster à mistura.
  • A introdução de mais novos aliases de palavras-chave para todos esses tipos pode ser problemática.

Apenas deixando comida para o pensamento aqui. Eu acho que System.Rune , enquanto um nome de tipo ruim para sua finalidade, não torna o status quo de nomenclatura anterior pior. Acho ótimo que finalmente exista um tipo adequado capaz de codificar todos os escalares Unicode. No entanto, vejo uma boa oportunidade de espalhar uma tendência de manipulação e nomenclatura mais precisas de Unicode. Uma oportunidade que todos aqui são livres para deixar de lado.

Olá a todos - o nome System.Text.Rune é o que foi enviado e o que estamos usando daqui para frente. Houve uma discussão anterior significativa (e acalorada!) de usar o nome UnicodeScalar em vez de Rune , mas no final Rune venceu. A equipe não está cogitando a ideia de escolher um nome diferente para ele neste momento. E embora eu saiba que as pessoas são apaixonadas por isso e continuaremos a monitorar a conversa aqui, em última análise, esteja ciente de que qualquer energia gasta continuando a litigar a questão do nome não renderá dividendos.

Para esclarecimento, e de acordo com os documentos: o tipo System.Text.Rune em .NET é exatamente equivalente a um valor escalar Unicode. Isso é imposto pela construção. Isso o torna mais análogo ao tipo UnicodeScalar do Swift do que ao tipo rune do Go.

Há um esforço em andamento para adicionar uma seção aos documentos Rune detalhando seus casos de uso e como ela se relaciona com outras APIs de processamento de texto em .NET e conceitos em Unicode. O problema de rastreamento está em https://github.com/dotnet/docs/issues/15845. Há também um link desse problema de rastreamento para um rascunho atual dos documentos conceituais.

Para mim, a principal desvantagem com UnicodeScalar é a grande disparidade entre o comprimento do nome do tipo e o tamanho dos dados do tipo. Essencialmente, é um int com algumas lacunas em seu domínio.

No entanto, a verbosidade no uso seria extrema:

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

vs o equivalente char sobre string (e, idealmente, as pessoas usariam o novo tipo sobre char , pois são valores inteiros em vez de conter valores divididos)

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

Rune é um compromisso na verbosidade do nome do tipo:

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

@GrabYourPitchforks

Olá! Para ser honesto, eu me envolvi nessa discussão não porque estou tentando convencer o pessoal do .NET que o nome precisa ser mudado, pois parece que aquele navio já partiu, mas simplesmente porque eu queria expressar minha opinião para outros neste tópico que não concordaram com isso. Eu acho maravilhoso que C# finalmente tenha um tipo de caractere _real_ em oposição ao tipo de caractere quebrado que ele teve por tanto tempo, e o nome é completamente secundário a isso. Eu entendo que há um grande equilíbrio a ser alcançado entre brevidade e precisão e, embora eu tenha colocado o ponto ideal em torno de CodePoint , entendo por que outros discordariam.

Mas, novamente, quero agradecer por todo o trabalho duro na modernização do suporte Unicode do .NET! Isso é algo que faz uma enorme diferença para muitas pessoas ao redor do mundo.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

sahithreddyk picture sahithreddyk  ·  3Comentários

jzabroski picture jzabroski  ·  3Comentários

aggieben picture aggieben  ·  3Comentários

yahorsi picture yahorsi  ·  3Comentários

omariom picture omariom  ·  3Comentários