Este código:
string a = "abc";
string b = "A𠈓C";
Console.WriteLine("Length a = {0}", a.Length);
Console.WriteLine("Length b = {0}", b.Length);
saídas:
Length a = 3
Length b = 4
Por quê? A única coisa que eu poderia imaginar é que o caractere chinês tenha 2 bytes e que o .Length
método retorne a contagem de bytes.
𠈓
é 131603 e, como os caracteres são bytes não assinados, isso significa que você pode atingir esse valor em 2 caracteres em vez de 4 (o valor não assinado de 16 bits no máximo é 65535 (ou 65536 variações) e o uso de 2 caracteres para representá-lo permite para um número máximo de variações não de 65536 * 2 (131072), mas sim de variações de 65536 * 65536 (4.294.967.296, efetivamente um valor de 32 bits) #Respostas:
Todo mundo está dando uma resposta superficial, mas também há uma lógica mais profunda: o número de "caracteres" é uma pergunta difícil de definir e pode ser surpreendentemente cara de calcular, enquanto uma propriedade length deve ser rápida.
Por que é difícil de definir? Bem, existem algumas opções e nenhuma é realmente mais válida que outra:
O número de unidades de código (bytes ou outro bloco de dados de tamanho fixo; C # e Windows geralmente usam UTF-16 para retornar o número de partes de dois bytes) certamente é relevante, pois o computador ainda precisa lidar com os dados dessa forma para muitos propósitos (gravar em um arquivo, por exemplo, se preocupa com bytes em vez de caracteres)
O número de pontos de código Unicode é bastante fácil de calcular (embora O (n) porque você precise escanear a string em busca de pares substitutos) e possa ser importante para um editor de texto ... mas na verdade não é a mesma coisa que o número de caracteres impresso na tela (chamado grafemas). Por exemplo, algumas letras acentuadas podem ser representadas de duas formas: um único ponto de código ou dois pontos emparelhados, um representando a letra e um dizendo "adicione um acento à carta do meu parceiro". O par teria dois caracteres ou um? Você pode normalizar seqüências de caracteres para ajudar nisso, mas nem todas as letras válidas têm uma única representação de ponto de código.
Mesmo o número de grafemas não é o mesmo que o comprimento de uma string impressa, que depende da fonte entre outros fatores, e como alguns caracteres são impressos com alguma sobreposição em muitas fontes (kerning), o comprimento de uma string na tela não é necessariamente igual à soma do comprimento dos grafemas!
Alguns pontos Unicode nem são caracteres no sentido tradicional, mas algum tipo de marcador de controle. Como um marcador de ordem de bytes ou um indicador da direita para a esquerda. Isso conta?
Em resumo, o comprimento de uma string é na verdade uma pergunta ridiculamente complexa e o cálculo pode levar muito tempo da CPU, bem como tabelas de dados.
Além disso, qual é o objetivo? Por que essas métricas são importantes? Bem, só você pode responder isso no seu caso, mas pessoalmente, acho que eles geralmente são irrelevantes. A limitação da entrada de dados que eu acho mais lógica é feita pelos limites de bytes, pois é isso que precisa ser transferido ou armazenado de qualquer maneira. A limitação do tamanho da tela é melhor realizada pelo software do lado da tela - se você tiver 100 pixels para a mensagem, quantos caracteres caberão dependerão da fonte etc., que não é conhecida pelo software da camada de dados. Finalmente, dada a complexidade do padrão unicode, você provavelmente terá bugs nos casos extremos de qualquer maneira, se tentar qualquer outra coisa.
Portanto, é uma pergunta difícil, com pouco uso de propósito geral. O número de unidades de código é trivial para calcular - é apenas o comprimento da matriz de dados subjacente - e o mais significativo / útil como regra geral, com uma definição simples.
É por isso
b
que o comprimento está4
além da explicação superficial de "porque a documentação diz isso".fonte
Length
deva ser obsoleto, manter a analogia com matrizes.A partir da documentação da
String.Length
propriedade:fonte
String b
), pois usa a representação UTF-16 em matrizes de caracteres. É um caractere de 4 bytes em UTF-8.Seu personagem no índice 1 em
"A𠈓C"
é um SurrogatePairVocê pode tentar esse código e ele retornará
True
Método Char.IsSurrogatePair (String, Int32)
Isso é explicado mais detalhadamente na propriedade String.Length :
fonte
Como as outras respostas apontaram, mesmo se houver 3 caracteres visíveis, eles são representados com 4
char
objetos. É por isso queLength
é 4 e não 3.MSDN afirma que
No entanto, se você realmente deseja saber o número de "elementos de texto" e não o número de
Char
objetos, você pode usar aStringInfo
classe.Você também pode enumerar cada elemento de texto como este
O uso
foreach
da string dividirá a "letra" do meio em doischar
objetos e o resultado impresso não corresponderá à string.fonte
Isso ocorre porque a
Length
propriedade retorna o número de objetos de caracteres , não o número de caracteres unicode. No seu caso, um dos caracteres Unicode é representado por mais de um objeto char (SurrogatePair).fonte
Como outros disseram, não é o número de caracteres na string, mas o número de objetos Char. O caractere 𠈓 é o ponto de código U + 20213. Como o valor está fora do intervalo do tipo de caractere de 16 bits, ele é codificado em UTF-16 como o par substituto
D840 DE13
.A maneira de obter o tamanho dos caracteres foi mencionada nas outras respostas. No entanto, deve ser usado com cuidado, pois pode haver muitas maneiras de representar um caractere no Unicode. "à" pode ter 1 caractere composto ou 2 caracteres (a + diacríticos). A normalização pode ser necessária, como no caso do twitter .
Você deve ler isto
O mínimo absoluto que todo desenvolvedor de software deve saber absolutamente, positivamente sobre Unicode e conjuntos de caracteres (sem desculpas!)
fonte
Isso ocorre porque
length()
funciona apenas para pontos de código Unicode que não são maiores queU+FFFF
. Esse conjunto de pontos de código é conhecido como Plano Multilíngue Básico (BMP) e usa apenas 2 bytes.Os pontos de código Unicode fora do
BMP
são representados no UTF-16 usando pares substitutos de 4 bytes.Para contar corretamente o número de caracteres (3), use
StringInfo
fonte
Ok, em .Net e C # todas as strings são codificadas como UTF-16LE . A
string
é armazenado como uma sequência de caracteres. Cada umchar
encapsula o armazenamento de 2 bytes ou 16 bits.O que vemos "no papel ou na tela" como uma única letra, caractere, glifo, símbolo ou sinal de pontuação pode ser considerado um único Elemento de Texto. Conforme descrito no Anexo UNICODE nº 29 SEGMENTAÇÃO DE TEXTO DO UNICODE , cada elemento de texto é representado por um ou mais pontos de código. Uma lista exaustiva de códigos pode ser encontrada aqui .
Cada ponto de código precisa ser codificado em binário para representação interna por um computador. Como indicado, cada um
char
armazena 2 bytes. Os pontos de código iguais ou inferioresU+FFFF
podem ser armazenados em um únicochar
. Os pontos de código acimaU+FFFF
são armazenados como um par substituto, usando dois caracteres para representar um único ponto de código.Dado o que sabemos agora que podemos deduzir, um Elemento de Texto pode ser armazenado como um
char
, como um Par Substituto de dois caracteres ou, se o Elemento de Texto for representado por vários Pontos de Código, alguma combinação de caracteres únicos e Pares Substitutos. Como se isso não fosse suficientemente complicado, alguns Elementos de Texto podem ser representados por diferentes combinações de Pontos de Código, conforme descrito no Anexo Padrão 15 do Unicode, FORMULÁRIOS DE NORMALIZAÇÃO DO UNICODE .Interlúdio
Portanto, as strings com a mesma aparência quando renderizadas podem realmente ser compostas de uma combinação diferente de caracteres. Uma comparação ordinal (byte a byte) de duas dessas seqüências detectaria uma diferença, isso pode ser inesperado ou indesejável.
Você pode recodificar as seqüências .Net. para que eles usem o mesmo formulário de normalização. Uma vez normalizado, duas seqüências com os mesmos elementos de texto serão codificadas da mesma maneira. Para fazer isso, use a função string.Normalize . No entanto, lembre-se, alguns elementos de texto diferentes se parecem. : -s
Então, o que tudo isso significa em relação à pergunta? O elemento Text
'𠈓'
é representado pela única extensão de ideogramas unificados Code Point U + 20213 cjk b . Isso significa que não pode ser codificado como um únicochar
e deve ser codificado como Par Substituto, usando dois caracteres. É por isso questring b
échar
mais um issostring a
.Se você precisar contar de forma confiável (consulte a advertência) o número de Elementos de Texto em um,
string
você deve usar aSystem.Globalization.StringInfo
classe como esta.dando a saída,
como esperado.
Embargo
A implementação .Net da segmentação de texto Unicode nas classes
StringInfo
eTextElementEnumerator
deve ser geralmente útil e, na maioria dos casos, produzirá uma resposta que o chamador espera. No entanto, conforme declarado no Anexo Padrão 29 da Unicode, "O objetivo de corresponder às percepções do usuário nem sempre pode ser alcançado exatamente porque o texto por si só nem sempre contém informações suficientes para decidir inequivocamente os limites".fonte