.NET Framework
Instrumentos de cuerda
Buscar..
Observaciones
En las cadenas .NET System.String
son secuencias de caracteres System.Char
, cada carácter es una unidad de código codificada en UTF-16. Esta distinción es importante porque la definición de caracteres en el lenguaje hablado y la definición de caracteres en .NET (y en muchos otros idiomas) son diferentes.
Un carácter , que debería llamarse correctamente grafema , se muestra como un glifo y está definido por uno o más puntos de código Unicode. Cada punto de código se codifica en una secuencia de unidades de código . Ahora debería estar claro por qué un solo System.Char
no siempre representa un grafema, veamos en el mundo real cómo son diferentes:
- Un grafema, debido a la combinación de caracteres , puede dar lugar a dos o más puntos de código: à está compuesto por dos puntos de código: U + 0061 LETRA PEQUEÑA LETRA A y U + 0300 COMBINACIÓN DEL ACUMENTO GRAVE . Este es el error más común porque
"à".Length == 2
mientras que usted puede esperar1
. - Hay caracteres duplicados, por ejemplo à puede ser un único punto de código U + 00E0 LETRA A LATINA PEQUEÑA CON GRAVE o dos puntos de código como se explicó anteriormente. Obviamente, deben comparar lo mismo:
"\u00e0" == "\u0061\u0300"
(incluso si"\u00e0".Length != "\u0061\u0300".Length
). Esto es posible debido a la normalización de la cadena realizada por el métodoString.Normalize()
. - Una secuencia Unicode puede contener una secuencia compuesta o descompuesta, por ejemplo, el carácter 한 U + D55C HAN CHARACTER puede ser un único punto de código (codificado como una sola unidad de código en UTF-16) o una secuencia descompuesta de sus sílabas ᄒ , ᅡ y ᆫ . Deben ser comparados iguales.
- Un punto de código puede codificarse en más de una unidad de código: el carácter 𠂊 U + 2008A HAN CHARACTER se codifica como dos
System.Char
("\ud840\udc8a"
) incluso si es solo un punto de código: UTF-16 La codificación no es de tamaño fijo! Esta es una fuente de innumerables errores (también errores graves de seguridad), si, por ejemplo, su aplicación aplica una longitud máxima y trunca ciegamente la cadena, entonces puede crear una cadena no válida. - Algunos idiomas tienen digraph y trigraphs, por ejemplo, en ch ch ch es una letra independiente (después de h y antes de i , cuando ordene una lista de cadenas, tendrá fyzika antes de chemie .
Hay muchos más problemas relacionados con el manejo del texto. Consulte, por ejemplo, ¿Cómo puedo realizar una comparación de caracteres por caracteres en Unicode? para una introducción más amplia y más enlaces a argumentos relacionados.
En general, cuando se trata de texto internacional , puede usar esta función simple para enumerar elementos de texto en una cadena (evitando romper los sustitutos y la codificación de Unicode):
public static class StringExtensions
{
public static IEnumerable<string> EnumerateCharacters(this string s)
{
if (s == null)
return Enumerable.Empty<string>();
var enumerator = StringInfo.GetTextElementEnumerator(s.Normalize());
while (enumerator.MoveNext())
yield return (string)enumerator.Value;
}
}
Contar personajes distintos
Si necesita contar caracteres distintos, entonces, por las razones explicadas en la sección Comentarios , no puede simplemente usar la propiedad Length
porque es la longitud de la matriz de System.Char
que no son caracteres sino unidades de código (no puntos de código Unicode). ni grafemas). Si, por ejemplo, simplemente escribe text.Distinct().Count()
obtendrá resultados incorrectos, código correcto:
int distinctCharactersCount = text.EnumerateCharacters().Count();
Un paso más es contar las ocurrencias de cada personaje , si el rendimiento no es un problema, simplemente puede hacerlo así (en este ejemplo, independientemente del caso):
var frequencies = text.EnumerateCharacters()
.GroupBy(x => x, StringComparer.CurrentCultureIgnoreCase)
.Select(x => new { Character = x.Key, Count = x.Count() };
Contar personajes
Si necesita contar caracteres , entonces, por las razones explicadas en la sección Comentarios , no puede simplemente usar la propiedad Longitud porque es la longitud de la matriz de System.Char
que no son caracteres sino unidades de código (no son puntos de código Unicode ni grafemas). El código correcto es entonces:
int length = text.EnumerateCharacters().Count();
Una pequeña optimización puede reescribir el método de extensión EnumerateCharacters()
específicamente para este propósito:
public static class StringExtensions
{
public static int CountCharacters(this string text)
{
if (String.IsNullOrEmpty(text))
return 0;
int count = 0;
var enumerator = StringInfo.GetTextElementEnumerator(text);
while (enumerator.MoveNext())
++count;
return count;
}
}
Contar las ocurrencias de un personaje.
Debido a las razones explicadas en la sección de Comentarios , no puede simplemente hacer esto (a menos que quiera contar las ocurrencias de una unidad de código específica):
int count = text.Count(x => x == ch);
Necesitas una función más compleja:
public static int CountOccurrencesOf(this string text, string character)
{
return text.EnumerateCharacters()
.Count(x => String.Equals(x, character, StringComparer.CurrentCulture));
}
Tenga en cuenta que la comparación de cadenas (en contraste con la comparación de caracteres que es invariante de la cultura) siempre debe realizarse de acuerdo con las reglas de una cultura específica.
Dividir la cadena en bloques de longitud fija
No podemos dividir una cadena en puntos arbitrarios (porque un System.Char
puede no ser válido solo porque es un carácter de combinación o parte de un sustituto), entonces el código debe tener eso en cuenta (tenga en cuenta que con la longitud me refiero al número de grafemas, no al número de unidades de código ):
public static IEnumerable<string> Split(this string value, int desiredLength)
{
var characters = StringInfo.GetTextElementEnumerator(value);
while (characters.MoveNext())
yield return String.Concat(Take(characters, desiredLength));
}
private static IEnumerable<string> Take(TextElementEnumerator enumerator, int count)
{
for (int i = 0; i < count; ++i)
{
yield return (string)enumerator.Current;
if (!enumerator.MoveNext())
yield break;
}
}
Convertir cadena a / desde otra codificación
Las cadenas .NET contienen System.Char
(unidades de código UTF-16). Si desea guardar (o administrar) texto con otra codificación, debe trabajar con una matriz de System.Byte
.
Las conversiones se realizan por clases derivadas de System.Text.Encoder
y System.Text.Decoder
que, juntas, pueden convertir a / desde otra codificación (de un byte X byte byte codificado byte[]
a un sistema codificado en UTF- System.String
y vice -versa).
Debido a que el codificador / decodificador generalmente funciona muy cerca uno del otro, se agrupan en una clase derivada de System.Text.Encoding
, las clases derivadas ofrecen conversiones a / desde codificaciones populares (UTF-8, UTF-16 y así sucesivamente).
Ejemplos:
Convertir una cadena a UTF-8
byte[] data = Encoding.UTF8.GetBytes("This is my text");
Convertir datos UTF-8 a una cadena
var text = Encoding.UTF8.GetString(data);
Cambiar la codificación de un archivo de texto existente
Este código leerá el contenido de un archivo de texto codificado en UTF-8 y lo guardará nuevamente codificado como UTF-16. Tenga en cuenta que este código no es óptimo si el archivo es grande porque leerá todo su contenido en la memoria:
var content = File.ReadAllText(path, Encoding.UTF8);
File.WriteAllText(content, Encoding.UTF16);
Object.ToString () método virtual
Todo en .NET es un objeto, por lo tanto, cada tipo tiene el método ToString()
definido en la clase Object
que puede ser anulado. La implementación predeterminada de este método solo devuelve el nombre del tipo:
public class Foo
{
}
var foo = new Foo();
Console.WriteLine(foo); // outputs Foo
ToString()
se llama implícitamente cuando concatina un valor con una cadena:
public class Foo
{
public override string ToString()
{
return "I am Foo";
}
}
var foo = new Foo();
Console.WriteLine("I am bar and "+foo);// outputs I am bar and I am Foo
El resultado de este método también es ampliamente utilizado por las herramientas de depuración. Si, por alguna razón, no desea anular este método, pero desea personalizar cómo el depurador muestra el valor de su tipo, use el atributo DebuggerDisplay ( MSDN ):
// [DebuggerDisplay("Person = FN {FirstName}, LN {LastName}")]
[DebuggerDisplay("Person = FN {"+nameof(Person.FirstName)+"}, LN {"+nameof(Person.LastName)+"}")]
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set;}
// ...
}
Inmutabilidad de las cuerdas.
Las cuerdas son inmutables. Simplemente no puedes cambiar la cadena existente. Cualquier operación en la cadena crea una nueva instancia de la cadena con un nuevo valor. Esto significa que si necesita reemplazar un solo carácter en una cadena muy larga, la memoria se asignará para un nuevo valor.
string veryLongString = ...
// memory is allocated
string newString = veryLongString.Remove(0,1); // removes first character of the string.
Si necesita realizar muchas operaciones con valor de cadena, use la clase StringBuilder
que está diseñada para la manipulación eficiente de cadenas:
var sb = new StringBuilder(someInitialString);
foreach(var str in manyManyStrings)
{
sb.Append(str);
}
var finalString = sb.ToString();
Cuerdas
A pesar de que String
es un tipo de referencia, el operador ==
compara los valores de cadena en lugar de las referencias.
Como sabrás, la string
es solo una matriz de caracteres. Pero si crees que la verificación y comparación de la igualdad de las cadenas se hace carácter por carácter, estás equivocado. Esta operación es específica de la cultura (ver Comentarios más abajo): algunas secuencias de caracteres pueden tratarse como iguales dependiendo de la cultura .
Piense dos veces antes de la verificación de la igualdad en el cortocircuito comparando las propiedades de Length
de dos cadenas.
Use sobrecargas del método String.Equals
que acepte el valor de enumeración StringComparison
adicional, si necesita cambiar el comportamiento predeterminado.