Este código:
string a = "abc";
string b = "A𠈓C";
Console.WriteLine("Length a = {0}", a.Length);
Console.WriteLine("Length b = {0}", b.Length);
salidas:
Length a = 3
Length b = 4
¿Por qué? Lo único que podría imaginar es que el carácter chino tiene 2 bytes de longitud y que el .Length
método devuelve el recuento de bytes.
𠈓
es 131603, y como los caracteres son bytes sin signo, eso significa que puede lograr ese valor en 2 caracteres en lugar de 4 (el valor máximo de 16 bits sin signo es 65535 (o 65536 variaciones) y usar 2 caracteres para representarlo permite para un número máximo de variaciones no 65536 * 2 (131072) sino más bien 65536 * 65536 variaciones (4,294,967,296, efectivamente un valor de 32 bits)Respuestas:
Todos los demás están dando la respuesta superficial, pero también hay una razón más profunda: el número de "caracteres" es una pregunta difícil de definir y puede ser sorprendentemente costoso de calcular, mientras que una propiedad de longitud debería ser rápida.
¿Por qué es difícil de definir? Bueno, hay algunas opciones y ninguna es realmente más válida que otra:
El número de unidades de código (bytes u otro fragmento de datos de tamaño fijo; C # y Windows generalmente usan UTF-16, por lo que devuelve el número de piezas de dos bytes) es ciertamente relevante, ya que la computadora aún necesita manejar los datos de esa forma para muchos propósitos (escribir en un archivo, por ejemplo, se preocupa por los bytes en lugar de los caracteres)
El número de puntos de código Unicode es bastante fácil de calcular (aunque O (n) porque debe escanear la cadena en busca de pares sustitutos) y puede ser importante para un editor de texto ... pero en realidad no es lo mismo que el número de caracteres impreso en pantalla (llamados grafemas). Por ejemplo, algunas letras acentuadas se pueden representar de dos formas: un único punto de código o dos puntos emparejados, uno que representa la letra y otro que dice "agregar un acento a mi letra asociada". ¿Sería la pareja dos personajes o uno? Puede normalizar cadenas para ayudar con esto, pero no todas las letras válidas tienen una única representación de punto de código.
Incluso la cantidad de grafemas no es la misma que la longitud de una cadena impresa, que depende de la fuente, entre otros factores, y dado que algunos caracteres se imprimen con cierta superposición en muchas fuentes (interletraje), la longitud de una cadena en la pantalla ¡no es necesariamente igual a la suma de la longitud de los grafemas de todos modos!
Algunos puntos Unicode ni siquiera son caracteres en el sentido tradicional, sino más bien algún tipo de marcador de control. Como un marcador de orden de bytes o un indicador de derecha a izquierda. ¿Cuentan estos?
En resumen, la longitud de una cadena es en realidad una pregunta ridículamente compleja y calcularla puede llevar mucho tiempo de CPU, así como tablas de datos.
Además, ¿cuál es el punto? ¿Por qué son importantes estas métricas? Bueno, solo usted puede responder eso para su caso, pero personalmente, considero que generalmente son irrelevantes. La limitación de la entrada de datos que encuentro se realiza de manera más lógica por los límites de bytes, ya que eso es lo que debe transferirse o almacenarse de todos modos. La limitación del tamaño de la pantalla se realiza mejor con el software del lado de la pantalla: si tiene 100 píxeles para el mensaje, la cantidad de caracteres que ajuste dependerá de la fuente, etc. Finalmente, dada la complejidad del estándar Unicode, es probable que tenga errores en los casos extremos de todos modos si intenta algo más.
Por lo tanto, es una pregunta difícil con poco uso general. El número de unidades de código es trivial de calcular, es solo la longitud de la matriz de datos subyacente, y el más significativo / útil como regla general, con una definición simple.
Es por eso que
b
tiene una extensión4
más allá de la explicación superficial de "porque la documentación lo dice".fuente
Length
deba ser obsoleto, para mantener la analogía con las matrices.De la documentación de la
String.Length
propiedad:fuente
String b
), ya que utiliza la representación UTF-16 en matrices de caracteres. Es un carácter de 4 bytes en UTF-8.Tu personaje en el índice 1 en
"A𠈓C"
es un par sustitutoPuedes probar este código y volverá
True
Método Char.IsSurrogatePair (String, Int32)
Esto se explica con más detalle en la propiedad String.Length :
fuente
Como han señalado las otras respuestas, incluso si hay 3 caracteres visibles, se representan con 4
char
objetos. Es por eso queLength
es 4 y no 3.MSDN afirma que
Sin embargo, si lo que realmente quiere saber es la cantidad de "elementos de texto" y no la cantidad de
Char
objetos, puede usar laStringInfo
clase.También puede enumerar cada elemento de texto de esta manera
El uso
foreach
en la cadena dividirá la "letra" del medio en doschar
objetos y el resultado impreso no se corresponderá con la cadena.fuente
Esto se debe a que la
Length
propiedad devuelve el número de objetos char , no el número de caracteres unicode. En su caso, uno de los caracteres Unicode está representado por más de un objeto char (SurrogatePair).fuente
Como otros dijeron, no es el número de caracteres en la cadena sino el número de objetos Char. El carácter 𠈓 es el punto de código U + 20213. Dado que el valor está fuera del rango del tipo char de 16 bits, está codificado en UTF-16 como el par sustituto
D840 DE13
.La forma de obtener la longitud en caracteres se mencionó en las otras respuestas. Sin embargo, debe usarse con cuidado ya que puede haber muchas formas de representar un personaje en Unicode. "à" puede tener 1 carácter compuesto o 2 caracteres (a + diacríticos). La normalización puede ser necesaria como en el caso de twitter .
Debería leer esto
El mínimo absoluto que todo desarrollador de software debe saber absolutamente, positivamente sobre los conjuntos de caracteres y Unicode (¡sin excusas!)
fuente
Esto se debe a que
length()
solo funciona para puntos de código Unicode que no son más grandes queU+FFFF
. Este conjunto de puntos de código se conoce como el Plano Multilingüe Básico (BMP) y usa solo 2 bytes.Los puntos de código Unicode fuera del
BMP
están representados en UTF-16 usando pares sustitutos de 4 bytes.Para contar correctamente el número de caracteres (3), use
StringInfo
fuente
De acuerdo, en .Net y C # todas las cadenas están codificadas como UTF-16LE . A
string
se almacena como una secuencia de caracteres. Cada unochar
encapsula el almacenamiento de 2 bytes o 16 bits.Lo que vemos "en papel o pantalla" como una sola letra, carácter, glifo, símbolo o signo de puntuación puede considerarse como un único Elemento de texto. Como se describe en el Anexo Estándar Unicode # 29 SEGMENTACIÓN DE TEXTO DE UNICODE , cada Elemento de Texto está representado por uno o más Puntos de Código. Puede encontrar una lista exhaustiva de códigos aquí .
Cada punto de código debe codificarse en binario para la representación interna de una computadora. Como se indicó, cada uno
char
almacena 2 bytes. Los puntos de código en o debajoU+FFFF
se pueden almacenar en un solochar
. Los puntos de código anterioresU+FFFF
se almacenan como un par sustituto, utilizando dos caracteres para representar un único punto de código.Dado lo que ahora sabemos que podemos deducir, un elemento de texto puede almacenarse como uno
char
, como un par sustituto de dos caracteres o, si el elemento de texto está representado por múltiples puntos de código, alguna combinación de caracteres individuales y pares sustitutos. Como si eso no fuera lo suficientemente complicado, algunos Elementos de texto pueden representarse mediante diferentes combinaciones de Puntos de código como se describe en el Anexo estándar Unicode # 15, FORMAS DE NORMALIZACIÓN DE UNICODE .Interludio
Por lo tanto, las cadenas que se ven iguales cuando se procesan en realidad pueden estar formadas por una combinación diferente de caracteres. Una comparación ordinal (byte por byte) de dos cadenas de este tipo detectaría una diferencia, esto puede ser inesperado o indeseable.
Puede volver a codificar cadenas .Net. para que usen el mismo formulario de normalización. Una vez normalizado, dos cadenas con los mismos elementos de texto se codificarán de la misma manera. Para hacer esto, use la función string.Normalize . Sin embargo, recuerde que algunos elementos de texto diferentes se parecen entre sí. : -s
Entonces, ¿qué significa todo esto en relación con la pregunta? El elemento de texto
'𠈓'
está representado por la única extensión de ideogramas unificados Code Point U + 20213 cjk b . Esto significa que no puede codificarse como únicochar
y debe codificarse como Par sustituto, utilizando dos caracteres. Es por eso questring b
es unochar
más largo questring a
.Si necesita contar de manera confiable (ver advertencia) el número de elementos de texto en un
string
, debe usar laSystem.Globalization.StringInfo
clase de esta manera.dando la salida,
como se esperaba.
Consideración
La implementación .Net de la segmentación de texto Unicode en las clases
StringInfo
yTextElementEnumerator
debería ser generalmente útil y, en la mayoría de los casos, producirá una respuesta que la persona que llama espera. Sin embargo, como se indica en el Anexo estándar 29 de Unicode, "El objetivo de hacer coincidir las percepciones de los usuarios no siempre se puede cumplir exactamente porque el texto por sí solo no siempre contiene suficiente información para decidir inequívocamente los límites".fuente