Manipulación de Strings II

Métodos para trabajar con strings

Manipulación de Strings II


Métodos para trabajar con strings

« Volver al inicio

Tal y como hemos adelantado en el capítulo anterior, todos los strings tienen varios métodos predefinidos que podemos usar para poder manipular cadenas de texto. Estos nos servirán de ayuda para hacer nuestros programas en un futuro, y sobre todo, porque de cara al examen será importante tenerlos controlados.

A continuación tenéis una “chuleta” de los métodos de string más importantes con los que estamos trabajando en clase.

Tabla de contenidos

  1. Longitud de una cadena string.Length
  2. Concatenar cadenas string + string
  3. Pasar a mayúsculas/minúsculas string.ToUpper() y string.ToLower()
  4. Subcadenas string.Substring(pos, lon)
  5. Buscar en strings
    1. Buscar una coincidencia string.Contains(string)
    2. Buscar primer índice string.IndexOf(string)
    3. Buscar último índice string.LastIndexOf(string)
  6. Insertar una subcadena string.Insert(pos, string)
  7. Eliminar caracteres string.Remove(pos, lon)
  8. Reemplazar caracteres string.Replace(string, string)
  9. Eliminar espacios string.Trim(), string.TrimStart(), string.TrimEnd()
  10. Descomponer cadenas string.Split(char)
  11. Comparar cadenas string.CompareTo(string), string.Compare(string), string.CompareOrdinal(string, string)

Longitud de una cadena

"Hola".Length4

Esta es la base de todos los métodos que conoceremos en adelante. Si no sabemos cómo sacar cuántos bytes ocupa un string, no podremos trabajar con la mayoría de los métodos que iremos explicando luego.

string saludo = "Hola";
int longitud = saludo.Length;
// longitud == 4

Si saludo tiene un total de 4 caracteres, saludo.Length devolverá el entero 4. Esto nos servirá, por ejemplo, para recorrer un string carácter por carácter como si se tratara de un array:

for(int i = 0; i < saludo.Length; i++) {
    Console.WriteLine(saludo[i]);
}

Concatenar cadenas

"Hola" + "hola""Holahola"

El operador + usado con cadenas y con caracteres sirve para concatenar una o varias cadenas.

Sean los dos strings:

string a = "Hola";
string b = " mundo";

Si a otro string c le asignamos el valor a + b nos dará como resultado: c = "Hola mundo"

string a = "Hola";
string b = " mundo";
string c = a + c;	// "Hola mundo"

Atención al espacio que hay en b = " mundo". Si no estuviera, el resultado de concatenar a y b sería: "Holamundo".

Console.WriteLine("Hola" + " mundo");
// Resultado: Hola mundo

Cuidado cuando estemos mezclando números y letras en una concatenación, ya que nos pueden dar resultados no deseados. Hay que recordar que las cadenas van sí o sí encerradas entre comillas, ya que si no, el compilador lo interpretará como un símbolo o como un número.

Console.WriteLine(1 + 2 + "3 = 6");

Esto nos devolverá por consola: 33 = 6 porque estamos concatenando el resultado de sumar 1 + 2 con la cadena “3 = 6”:

1 + 2 + "3 = 6"
  3   + "3 = 6"
       "33 = 6"

Si queremos corregir el trozo de código anterior:

Console.WriteLine("1 + 2 + " + "3 = 6");

… sí que nos devolverá: 1 + 2 + 3 = 6 porque estamos concatenando la cadena "1 + 2 +" con la cadena "3 = 6".

Fíjate en que le hemos añadido un '+' y un espacio a la cadena "1 + 2" para que el resultado tenga sentido, pues si no, el programa nos devolvería: 1 + 23 = 6

Falso:
"1 + 2" + "3 = 6"
     "1 + 23 = 6"

Verdadero:
"1 + 2 + " + "3 = 6"
     "1 + 2 + 3 = 6"

Pasar a mayúsculas/minúsculas

"Hola""HOLA"

"Hola""hola"

Dos métodos muy útiles para pasar una cadena entera a mayúsculas o a minúsculas: ToUpper() y ToLower():

Console.WriteLine("Hola mundo".ToUpper());
// HOLA MUNDO
Console.WriteLine("Hola mundo".ToLower());
// hola mundo

Esto puede ser muy útil por ejemplo a la hora de preguntar por consola un nombre o nick de usuario sin que queramos distinguir entre mayúsculas o minúsculas:

string usuario = Console.ReadLine();
if(usuario.ToLower() == "alex") {
    Console.WriteLine("Bienvenido");
}
else {
    Console.WriteLine("No he reconocido este usuario");
}

Aquí da igual que el usuario haya escrito Alex, alex, aLeX o cualquier otra combinación.

Subcadenas

"Hola""ol"

Una subcadena es una secuencia de caracteres que es parte de otra. Puede contener el texto entero de una cadena, o puede contener sólo un extracto de ella. Es decir, si tenemos la cadena:

string a = "Hola mundo";

Una subcadena podría ser “mundo”. O podría ser “Hola”. O hasta podría ser “undo”, o simplemente “mu”. Siempre con caracteres contiguos. Una cadena que sea “mundo Hola” no se considera substring de “Hola mundo” aunque tenga los mismos caracteres por estar en distinto orden.

string a    = "Hola mundo";
string sub1 =      "mun";
string sub2 =  "ol";
string sub3 =        "ndo";

Para sacar una subcadena a partir de otra cadena, tenemos el método Substring(), el cual puede recibir uno o dos parámetros.

Por ejemplo, si de "Hola mundo" le queremos quitar los 3 primeros caracteres, nos bastará con usar un parámetro de Substring, el cual indicará a la función que queremos ignorar los tres primeros caracteres. Por ejemplo:

string saludo = "Hola mundo";
Console.WriteLine(saludo.Substring(3));

Ejecutando eso, nos imprimirá por consola: a mundo, ya que los tres primeros caracteres, "Hol", se los hemos quitado a "Hola mundo" llamándolo a Substring(3).

En caso de que le pasemos dos parámetros a Substring, le estaremos indicando primero desde qué posición queremos extraer una subcadena a "Hola mundo", y con el segundo parámetro qué longitud queremos que tenga dicha subcadena.

Así, si desde la posición 3 queremos leer cuatro posiciones, tendremos que llamar al método Substring de esta forma:

string saludo = "Hola mundo";
Console.WriteLine(saludo.Substring(3, 4));

El resultado será: a mu, ya que:

    |
    v
 0123456789	<-- números de índice (longitud 10)
"Hola mundo"	<-- string en cuestión
   "a mu"	<-- substring resultante
   [1234]	<-- caracteres que hemos extraído de la cadena
    ^
    |
desde la posición 3

Así, si ahora queremos sacar los tres últimos caracteres de un string, tenemos que leer desde la posición 0 hasta la posición n-3, siendo n el tamaño de la cadena. En nuestro ejemplo:

string saludo = "Hola mundo";
int n = saludo.Length;

Console.WriteLine(saludo.Substring(0, n - 3));
// Resultado:
//Hola mu

Buscar en strings

Buscar una coincidencia

"Hola hola".Contains("ol")true

Si queremos comprobar simplemente si un pequeño fragmento de texto está contenido en una cadena, usaremos el método Contains, que devolverá un booleano (verdadero o falso) dependiendo de si ha encontrado o no un texto en otra cadena. El único parámetro que recibe es un término de búsqueda.

Por ejemplo, para comprobar si una cadena b está contenida en a, llamaremos al método: Contains en a de la siguiente forma:

string a = "Hola mundo";
string b = "la";

if(a.Contains(b)) {
    Console.WriteLine("He encontrado el texto");
}
else {
    Console.WriteLine("No he encontrado el texto");
}

En este caso, como “la” es parte de “Hola mundo”, nos devolverá la consola que ha encontrado el texto al evaluarse a.Contains(b) == true en el if.

Buscar primer índice

"Hola hola".IndexOf("o")1

Si además de querer buscar un fragmento de texto en una cadena, queremos saber en qué número de índice se encuentra dicho término de búsqueda, contamos con el método IndexOf, al cual también se le pasa un término de búsqueda como único parámetro.

En lugar de un booleano, nos devolverá un número entero. Irá leyendo el string a donde buscar de izquierda a derecha hasta encontrar una coincidencia del texto a buscar. Devolverá -1 si no ha encontrado ninguna coincidencia. Si devuelve un número mayor (de 0 en adelante), significa que lo ha encontrado y se encuentra en ese número de índice.

string a = "Hola mundo";
string b = "la";
int busqueda = a.Contains(b);

if(busqueda >= 0) {
    Console.WriteLine("He encontrado el texto en la posición: " + busqueda);
}
else {
    Console.WriteLine("No he encontrado el texto");
}

En este caso, estamos buscando otra vez si “la” está en el texto “Hola mundo”, pero nos devolverá 2 porque “la” se encuentra a partir de la posición 2 del string “Hola mundo”:

 0123456789	<-- números de índice (longitud 10)
"Hola mundo"	<-- string en cuestión
   ^		buscamos "la" de izquierda a derecha
   |
Primera coincidencia en índice=2

Buscar último índice

"Hola hola".LastIndexOf("o")6

Si ahora queremos buscar la última coincidencia, tenemos el método “hermano”: LastIndexOf. Este irá leyendo el string de derecha a izquierda hasta encontrar una coincidencia de la subcadena sobre el string, devolviendo su número de índice. Igual que IndexOf, pero leyendo desde el final.

string a = "Hola mundo";

Console.WriteLine(a.IndexOf("o"));
// Devolverá 1 porque encontrará la primera "o"

Console.WriteLine(a.LastIndexOf("o"));
// Devolverá 9 porque encontrará la última "o"

Insertar una subcadena

"Hola""Hoooola"

Al igual que podemos concatenar cadenas, también podemos insertar un string sobre otro, al principio, a mitad o al final. Para ello usaremos el método Insert(), que recibe dos parámetros: posición y cadena.

Posición será el número de índice donde queramos insertar nuestro string, que lo definiremos con el parámetro cadena. Por ejemplo, a la frase “programación mola”, le podemos añadir un “La” al principio para que tenga un poco más de sentido la frase:

string frase = "programación mola";

Console.WriteLine(0, "La ");
// La consola devolverá: "La programación mola"
// OJO con ese espacio después de "La". Si no, nos devolverá "Laprogramación mola".

Esto es equivalente a lo que hemos hecho antes concatenando cadenas:

string frase = "programación mola";

Console.WriteLine("La " + frase);

Pero claro, la cosa se nos complicará un poco si queremos meter algo en medio de la frase. Nos tocaría partir en dos la frase “programación/mola” y luego concatenar tres strings. Aquí la solución más rápida sería usar Insert():

string frase = "programación mola";

Console.WriteLine(frase.Insert(12, " no"));
// La consola devolverá: "programación no mola"

¿Por qué le pasamos ahora 12 como primer parámetro a Insert()? Porque le estamos indicando que queremos insertar nuestro string " no" en la posición 12 del índice del string original, desplazando el resto de caracteres hacia la derecha.

 00000000001111111
 01234567890123456	<-- índices leídos en vertical
"programación mola"
             ^
insertamos " no" en posición 12:
 000000000011111
 012345678901234
"programación no"
             ^

y desplazamos el resto del string " mola" a la derecha:
"programación no mola"
                ---->             

Sabiendo esto, podemos usar frase.Length e Insert() para colocar un string al final de frase:

string frase = "programación mola";

Console.WriteLine(frase.Insert(frase.Length, " mucho"));
// La consola devolverá: "programación mola mucho"

Tenemos que llevar cuidado de no salirnos mucho del tamaño del índice. Si a Insert le pasamos un número de índice mayor que la longitud del string (indice > string.Length), el compilador se lo tragará, pero acabaremos teniendo un error en tiempo de ejecución.

Eliminar caracteres

"Hola""Hla"

Al igual que podemos insertar, también podemos eliminar caracteres a partir de una posición. Para ello tenemos el método Remove(), el cual recibe dos parámetros: posición y longitud.

Por ejemplo, volvemos a esta cadena de longitud 10:

"Hola mundo"
 0123456789

Si queremos quitar el espacio que hay entre “Hola” y “mundo”, tendremos que llamar a la función Remove con los parámetros 4 y 1, porque queremos eliminar un sólo carácter desde la posición 4.

string frase = "Hola mundo";

Console.WriteLine(frase.Remove(4, 1));
// Devolverá "Holamundo" porque hemos quitado un espacio en la posición 4

Si queremos quitar la palabra “mundo” entera, entonces lo extenderemos a la longitud de la palabra “mundo”. Es decir, tendremos que quitar el espacio (1 byte) y la palabra mundo (5 bytes), que miden ambos 6 bytes:

string frase = "Hola mundo";

Console.WriteLine(frase.Remove(4, 6));
// Devolverá "Hola" porque hemos quitado 6 caracteres desde la posición 4.

Lo mismo podemos conseguir si quitamos el segundo parámetro. Cuando llamas a Remove() con un sólo parámetro, le indicarás a la función que sólo quieres quedarte con los primeros X caracteres del string.

string frase = "Hola mundo";

Console.WriteLine(frase.Remove(4));
// Devolverá "Hola" porque recortará el string a los cuatro primeros caracteres.

Reemplazar caracteres

"Hola""Holi"

Una alternativa mejor a reemplazar carácteres byte por byte en strings es usando el método Replace(). Dicha función espera dos cadenas de texto: la primera con el texto a buscar, y el segundo con el texto con el que queremos reemplazar.

Por ejemplo, si a nuestra frase “Hola mundo” queremos cambiar todas las “o” por una “a”, será tan simple como hacer esto:

string frase = "Hola mundo";

Console.WriteLine(frase.Replace("o", "a"));
// Devolverá "Hala munda" porque hemos cambiado todas las "o" por "a".

También podemos reemplazar cadenas con distinta longitud. Si en lugar de “Hola mundo” queremos que el programa diga “Hola a todos”, podemos usar Replace de la siguiente forma:

string frase = "Hola mundo";

Console.WriteLine(frase.Replace("mundo", "a todos"));
// Devolverá "Hola a todos"

Esto también nos servirá por ejemplo para eliminar espacios redundantes. Si queremos que una frase con dos espacios "hola mundo" pase a tener sólo un espacio, usaremos: Replace(" ", " ")

Eliminar espacios

"       hola     " → "hola"

A veces tenemos la necesidad de eliminar espacios al final y/o al principio de una cadena, por si acaso un usuario con mala fe nos quiere introducir un nombre con espacios al principio o al final de frase. Para ello tenemos el método Trim(), para “limpiar” esos espacios innecesarios:

// String con muchos espacios sobrantes:
string frase = "    hola mundo       ";

Console.WriteLine(frase.Trim());
/* Devolverá "hola mundo" porque Trim() elimina los espacios
   sobrantes al principio y al final de cadena */

También existen: TrimStart() para eliminar sólo los espacios iniciales, y TrimEnd() para eliminar los finales.

// String con muchos espacios sobrantes:
string frase = "    hola mundo       ";

Console.WriteLine(frase.TrimStart());	// => "hola mundo       "
Console.WriteLine(frase.TrimEnd());	// => "    hola mundo"

Descomponer cadenas

"Hola a todos".Split(' ')string[] { "Hola", "a", "todos" }

En algún momento nos hará falta dividir un string en varios trozos para tratar con cierta información. Por ejemplo, si nos pasan un nombre con sus apellidos: "Juan Rodríguez García" podremos separarlo en un array de strings que almacene en una posición "Juan", en otra "Rodríguez" y en otra "García".

Para ello, haremos uso de la función Split() que, sin argumentos, separará los strings por espacios:

string nombreApellidos = "Juan Rodríguez García";
string[] nombreApellidosSeparados = nombreApellidos.Split();

// Imprimimos los nombres línea por línea
foreach(string s in nombreApellidosSeparados) {
	Console.WriteLine(s);
}

Si el usuario por ejemplo está introduciendo una fecha, quizá nos interese más usar los guiones (-) como delimitadores de string:

string fecha = "12-10-2025";
string[] fechaSeparada = fecha.Split('-');

foreach(string n in fechaSeparada) {
	Console.WriteLine(n);
}
// Imprimirá:
12
10
2025

Pero no siempre podemos estar seguros de que el usuario siempre vaya a separar las fechas con guiones. Hay quienes usan barras o incluso puntos para separar las fechas. En situaciones así, necesitaremos definir más de un delimitador. Para ello usaremos un array de char:

string fecha1 = "12/10/2025";
string fecha2 = "12-10-2025";
string fecha3 = "12.10.2025";
char[] delimitadores = { '-', '/', ' ', '.' };
string[] fechaSeparada1 = fecha1.Split(delimitadores);
string[] fechaSeparada2 = fecha2.Split(delimitadores);
string[] fechaSeparada3 = fecha3.Split(delimitadores);

foreach(string n in fechaSeparada1) {
	Console.WriteLine(n);
}
// Nos devolverá lo mismo que los arrays fechaSeparada2 y fechaSeparada3:
12
10
2025

Otra forma “hacker”, aunque menos visual, de definir unos delimitadores es empleando un string, que recordemos que se puede comparar parcialmente con un array de chars, con la única diferencia de que C# sabe distinguir perfectamente entre string y char[]. Por lo tanto, hay que tener presente que la función Split() discrimina los strings, por lo que necesitaremos convertirlo a array de chars antes de pasárselo a Split si lo queremos hacer de este modo:

// Definimos los delimitadores carácter por carácter en un simple string:
string delimitadores = "-/. ";

// Preguntamos al usuario por una fecha:
Console.Write("Introduce una fecha: ");
string fecha = Console.ReadLine();

// La separamos convirtiendo los delimitadores a array de char:
string[] fechaSeparada = fecha.Split(delimitadores.ToCharArray());

// Si la fecha introducida no contiene tres grupos, ya la consideramos inválida:
if(fechaSeparada.Length != 3) {
	Console.WriteLine("Fecha introducida no válida");
}

Comparar cadenas

"Hola".CompareTo("adiós")1

string.Compare("Hola", "adiós")1

string.CompareOrdinal("Hola", "adiós")-25

Aquí viene el lío que se hacen a veces hasta los programadores profesionales al ser métodos que no estamos muy acostumbrados a utilizar en nuestro día a día, y por eso me lo he dejado para el final.

Antes hemos visto cómo comparar números y cómo lo podemos utilizar para ordenar arrays. Ahora veremos que también podemos comparar cadenas de texto y esto nos será útil para ordenar listas alfabéticamente.

El método más común que veremos será CompareTo(), cuya sintaxis es:

$$ cadena1.CompareTo(cadena2) $$

Éste devolverá -1, 0 o 1 según el caso:

  • -1 si cadena1 es menor que cadena2. Por ejemplo:

    "A".CompareTo("B"); => -1 porque "A" va antes que "B" en nuestro alfabeto
    
  • 0 si cadena1 y cadena2 son idénticas:

    "B".CompareTo("B"); => 0 porque estamos comparando dos cadenas iguales
    
  • 1 si cadena2 es menor que cadena1. Por ejemplo:

    "B".CompareTo("A"); => 1 porque "B" va después que "A" en nuestro alfabeto
    

Partiendo de este principio, las letras que van más tarde en el abecedario tienen mayor valor que las que van primero. Es decir, la “A” es el valor más pequeño en el abecedario mientras que “Z” es el más grande, y aquí es donde solemos hacernos un lío.

Para desenredar ese lío, un truco es que, en lugar de pensar si “A” es menor que “B” (A < B), tenemos que visualizarlo mentalmente como que “A” va antes que “B” en el alfabeto. Por lo tanto, si una letra (K por ejemplo) va después que la letra G en el alfabeto, sabremos que “K” es mayor que “G” (K > G).

El otro lío es saber cómo estamos realizando la comparación. Un truco para ver más claramente cuándo estamos comprobando si uno es mayor que otro o viceversa es comparando con 0 por la derecha:

if(cadena1.CompareTo(cadena2) < 0); /* Equivale a:
if(cadena1         < cadena2)       */
if(cadena1.CompareTo(cadena2) > 0); /* Equivale a:
if(cadena1         > cadena2        */
if(cadena1.CompareTo(cadena2) == 0); /* Equivale a:
if(cadena1        == cadena2        */

Básicamente, tenemos que visualizarlo en la sintaxis como si entre cadena1 y cadena2, en lugar de .CompareTo, hubiera un símbolo de < o > invisible, justo el mismo que estamos usando para comparar con 0.

Es más, también podemos usar los operadores <= y >= si queremos comprobar si va antes o igual en el alfabeto, o si va después o igual en el alfabeto.

if(cadena1.CompareTo(cadena2) <= 0); /* Equivale a:
if(cadena1        <= cadena2        */
if(cadena1.CompareTo(cadena2) >= 0); /* Equivale a:
if(cadena1        >= cadena2        */

Otro método equivalente es string.Compare(), el cual espera dos argumentos:

if(string.Compare(cadena1, cadena2) > 0);
// equivale a:
if(cadena1.CompareTo(cadena2) > 0);

El tercer lío viene cuando hacemos esta operación:

"A".CompareTo("a");       => 1
string.Compare("A", "a"); => 1
"a".CompareTo("A");       => -1
string.Compare("a", "A"); => -1

Aquí estamos viendo que tanto con CompareTo como Compare nos dicen que la letra “A” en mayúsculas va después de “a” en minúsculas.

¿Por qué ocurre esto? Primero porque ambos métodos distinguen entre mayúsculas y minúsculas.

¿Qué criterio sigue para determinar si uno es mayor que otro? Muchas veces pensamos que es porque estos métodos se rigen de la tabla de caracteres ASCII, pero si comprobamos dicha tabla, nos daremos cuenta de que algo no cuadra:

Tabla de caracteres ASCII ¿Qué ocurre aquí? Pues que según la tabla ASCII, la letra “A” va antes que la letra “a”, por lo que los métodos de comparación que hemos visto rompen con esta lógica.

La realidad es que, cuando se trabaja con strings con estos métodos, se comparan los caracteres en función de las reglas lexicográficas dependiendo del idioma que tengas establecido en el sistema, por lo que los resultados pueden variar entre un sistema en español y otro en ruso. Si queremos regirnos por el criterio establecido por la tabla ASCII, tendremos que usar el método CompareTo con chars, o bien el método string.CompareOrdinal si queremos hacerlo con strings:

string.CompareOrdinal("Alberto", "AlbertO"); => 32
'A'.CompareTo('a'); => -32

El primero devuelve 32 porque el carácter que diferencia ambas cadenas (‘o’ y ‘O’) están a 32 carácteres de distancia según ASCII: ‘o’ = 111 y ‘O’ = 79; por tanto, 111 - 79 = 32. El número es positivo porque ‘o’ va después que ‘O’ en la tabla ASCII, por lo que también podremos comparar CompareOrdinal con 0 por la derecha si sólo nos interesa saber cuál string es “menor” que otro.

El segundo devuelve -32 porque estamos comparando individualmente los caracteres ‘A’ y ‘a’, forzándolos a char por pasarlos con comillas simples. Como ‘A’ va antes que ‘a’ en la tabla ASCII, devolverá un valor negativo con la distancia que hay entre ambos caracteres (65 - 97 = -32).

Lo bueno que tiene string.Compare() pero que no tiene string.CompareOrdinal() es que con él podremos hacer comparaciones con cadenas sin distinguir entre mayúsculas y minúsculas si le pasamos un tercer parámetro (booleano) a true:

//    Comparar(Alberto con alberta, no distinguir mayúsculas/minúsculas: verdadero)
string.Compare("Alberto", "alberta", true);

// Es equivalente a:
"Alberto".ToLower().CompareTo("alberta".ToLower());
// O a:
"Alberto".ToUpper().CompareTo("alberta".ToUpper());

En resumen:

  • string.CompareTo(string) y string.Compare(string, string) son útiles para ordenar listas de strings rigiéndonos por reglas lexicográficas.
    • a.CompareTo(b) < 0 equivale a a < b
    • string.Compare(a, b) < 0 equivale a a < b
  • string.CompareOrdinal(string, string) es útil para ordenar listas de strings rigiéndonos por la tabla ASCII.
    • string.CompareOrdinal(a, b) < 0 equivale a a < b
  • char.CompareTo(char) es útil para comparar carácter por carácter o arrays de char rigiéndonos por la tabla ASCII.
  • string.Compare(string, string, true) y string.ToLower().CompareTo(string.ToLower()) nos pueden servir para ordenar listas sin tener en cuenta mayúsculas y minúsculas.
comments powered by Disqus