Extracción de subcadenas de una cadena

En este artículo se tratan algunas técnicas diferentes para extraer partes de una cadena.

Método String.Split

String.Split proporciona una serie de sobrecargas para ayudarle a dividir una cadena en un grupo de subcadenas en función de uno o más caracteres delimitadores que especifique. Puede elegir limitar el número total de subcadenas en el resultado final, recortar los caracteres de espacio en blanco de las subcadenas o excluir las subcadenas vacías.

En los siguientes ejemplos se muestran tres sobrecargas diferentes de String.Split(). En el primer ejemplo se llama a la sobrecarga Split(Char[]) sin pasar ningún carácter separador. Cuando no se especifica ningún carácter delimitador, String.Split() usa delimitadores predeterminados, que son caracteres de espacio en blanco, para dividir la cadena.

string s = "You win some. You lose some.";

string[] subs = s.Split();

foreach (string sub in subs)
{
    Console.WriteLine($"Substring: {sub}");
}

// This example produces the following output:
//
// Substring: You
// Substring: win
// Substring: some.
// Substring: You
// Substring: lose
// Substring: some.
Dim s As String = "You win some. You lose some."
Dim subs As String() = s.Split()

For Each substring As String In subs
    Console.WriteLine("Substring: {0}", substring)
Next

' This example produces the following output:
'
' Substring: You
' Substring: win
' Substring: some.
' Substring: You
' Substring: lose
' Substring: some.

Como puede ver, los caracteres de punto (.) se incluyen en dos de las subcadenas. Si desea excluir los caracteres de punto, puede agregar el carácter de punto como un carácter delimitador adicional. En el ejemplo siguiente se muestra cómo hacerlo.

string s = "You win some. You lose some.";

string[] subs = s.Split(' ', '.');

foreach (string sub in subs)
{
    Console.WriteLine($"Substring: {sub}");
}

// This example produces the following output:
//
// Substring: You
// Substring: win
// Substring: some
// Substring:
// Substring: You
// Substring: lose
// Substring: some
// Substring:
Dim s As String = "You win some. You lose some."
Dim subs As String() = s.Split(" "c, "."c)

For Each substring As String In subs
    Console.WriteLine("Substring: {0}", substring)
Next

' This example produces the following output:
'
' Substring: You
' Substring: win
' Substring: some
' Substring:
' Substring: You
' Substring: lose
' Substring: some
' Substring:

Los puntos desaparecen de las subcadenas, pero ahora se han incluido dos subcadenas vacías adicionales. Esta subcadena vacía representa la subcadena entre la palabra y el punto que la sigue. Para omitir las subcadenas vacías de la matriz resultante, puede llamar a la sobrecarga Split(Char[], StringSplitOptions) y especificar StringSplitOptions.RemoveEmptyEntries para el parámetro options.

string s = "You win some. You lose some.";
char[] separators = new char[] { ' ', '.' };

string[] subs = s.Split(separators, StringSplitOptions.RemoveEmptyEntries);

foreach (string sub in subs)
{
    Console.WriteLine($"Substring: {sub}");
}

// This example produces the following output:
//
// Substring: You
// Substring: win
// Substring: some
// Substring: You
// Substring: lose
// Substring: some
Dim s As String = "You win some. You lose some."
Dim separators As Char() = New Char() {" "c, "."c}
Dim subs As String() = s.Split(separators, StringSplitOptions.RemoveEmptyEntries)

For Each substring As String In subs
    Console.WriteLine("Substring: {0}", substring)
Next

' This example produces the following output:
'
' Substring: You
' Substring: win
' Substring: some
' Substring: You
' Substring: lose
' Substring: some

Expresiones regulares

Si la cadena se ajusta a un patrón fijo, puede usar una expresión regular para extraer y controlar sus elementos. Por ejemplo, si las cadenas adoptan el formato "número operando número", puede utilizar una expresión regular para extraer y administrar los elementos de la cadena. Este es un ejemplo:

String[] expressions = { "16 + 21", "31 * 3", "28 / 3",
                       "42 - 18", "12 * 7",
                       "2, 4, 6, 8" };
String pattern = @"(\d+)\s+([-+*/])\s+(\d+)";

foreach (string expression in expressions)
{
    foreach (System.Text.RegularExpressions.Match m in
    System.Text.RegularExpressions.Regex.Matches(expression, pattern))
    {
        int value1 = Int32.Parse(m.Groups[1].Value);
        int value2 = Int32.Parse(m.Groups[3].Value);
        switch (m.Groups[2].Value)
        {
            case "+":
                Console.WriteLine("{0} = {1}", m.Value, value1 + value2);
                break;
            case "-":
                Console.WriteLine("{0} = {1}", m.Value, value1 - value2);
                break;
            case "*":
                Console.WriteLine("{0} = {1}", m.Value, value1 * value2);
                break;
            case "/":
                Console.WriteLine("{0} = {1:N2}", m.Value, value1 / value2);
                break;
        }
    }
}

// The example displays the following output:
//       16 + 21 = 37
//       31 * 3 = 93
//       28 / 3 = 9.33
//       42 - 18 = 24
//       12 * 7 = 84
Dim expressions() As String = {"16 + 21", "31 * 3", "28 / 3",
                              "42 - 18", "12 * 7",
                              "2, 4, 6, 8"}

Dim pattern As String = "(\d+)\s+([-+*/])\s+(\d+)"
For Each expression In expressions
    For Each m As Match In Regex.Matches(expression, pattern)
        Dim value1 As Integer = Int32.Parse(m.Groups(1).Value)
        Dim value2 As Integer = Int32.Parse(m.Groups(3).Value)
        Select Case m.Groups(2).Value
            Case "+"
                Console.WriteLine("{0} = {1}", m.Value, value1 + value2)
            Case "-"
                Console.WriteLine("{0} = {1}", m.Value, value1 - value2)
            Case "*"
                Console.WriteLine("{0} = {1}", m.Value, value1 * value2)
            Case "/"
                Console.WriteLine("{0} = {1:N2}", m.Value, value1 / value2)
        End Select
    Next
Next

' The example displays the following output:
'       16 + 21 = 37
'       31 * 3 = 93
'       28 / 3 = 9.33
'       42 - 18 = 24
'       12 * 7 = 84

El patrón de expresión regular (\d+)\s+([-+*/])\s+(\d+) se define como se indica a continuación:

Patrón Descripción
(\d+) Buscar coincidencias con uno o más dígitos decimales. Este es el primer grupo de captura.
\s+ Coincide con uno o varios caracteres de espacio en blanco.
([-+*/]) Coincide con un signo de operador aritmético (+, -, *, o /). Este es el segundo grupo de captura.
\s+ Coincide con uno o varios caracteres de espacio en blanco.
(\d+) Buscar coincidencias con uno o más dígitos decimales. Éste es el tercer grupo de captura.

También puede usar una expresión regular para extraer subcadenas de una cadena basada en un patrón en lugar de un conjunto fijo de caracteres. Este es un escenario común cuando se produce cualquiera de estas condiciones:

  • Uno o varios caracteres delimitadores no siempre sirven como delimitador en la instancia de String.

  • La secuencia y el número de caracteres delimitadores son variables o desconocidos.

Por ejemplo, el método Split no se puede usar para dividir la cadena siguiente, porque el número de caracteres \n (nueva línea) es variable y no siempre sirven como delimitadores.

[This is captured\ntext.]\n\n[\n[This is more captured text.]\n]
\n[Some more captured text:\n   Option1\n   Option2][Terse text.]

Una expresión regular puede dividir fácilmente esta cadena, como se muestra en el ejemplo siguiente.

String input = "[This is captured\ntext.]\n\n[\n" +
               "[This is more captured text.]\n]\n" +
               "[Some more captured text:\n   Option1" +
               "\n   Option2][Terse text.]";
String pattern = @"\[([^\[\]]+)\]";
int ctr = 0;

foreach (System.Text.RegularExpressions.Match m in
   System.Text.RegularExpressions.Regex.Matches(input, pattern))
{
    Console.WriteLine("{0}: {1}", ++ctr, m.Groups[1].Value);
}

// The example displays the following output:
//       1: This is captured
//       text.
//       2: This is more captured text.
//       3: Some more captured text:
//          Option1
//          Option2
//       4: Terse text.
Dim input As String = String.Format("[This is captured{0}text.]" +
                                  "{0}{0}[{0}[This is more " +
                                  "captured text.]{0}{0}" +
                                  "[Some more captured text:" +
                                  "{0}   Option1" +
                                  "{0}   Option2][Terse text.]",
                                  vbCrLf)
Dim pattern As String = "\[([^\[\]]+)\]"
Dim ctr As Integer = 0
For Each m As Match In Regex.Matches(input, pattern)
    ctr += 1
    Console.WriteLine("{0}: {1}", ctr, m.Groups(1).Value)
Next

' The example displays the following output:
'       1: This is captured
'       text.
'       2: This is more captured text.
'       3: Some more captured text:
'          Option1
'          Option2
'       4: Terse text.

El patrón de expresión regular \[([^\[\]]+)\] se define como se indica a continuación:

Patrón Descripción
\[ Coincide con un corchete de apertura.
([^\[\]]+) Coincide con cualquier carácter que no sea un corchete de apertura o de cierre una o varias veces. Este es el primer grupo de captura.
\] Coincide con un corchete de cierre.

El método Regex.Split es casi idéntico a String.Split, salvo que divide una cadena basándose en un patrón de expresión regular en lugar de un juego de caracteres fijo. Por ejemplo, en el ejemplo siguiente se usa el método Regex.Split para dividir una cadena que contiene subcadenas delimitadas por varias combinaciones de guiones y otros caracteres.

String input = "abacus -- alabaster - * - atrium -+- " +
               "any -*- actual - + - armoire - - alarm";
String pattern = @"\s-\s?[+*]?\s?-\s";
String[] elements = System.Text.RegularExpressions.Regex.Split(input, pattern);

foreach (string element in elements)
    Console.WriteLine(element);

// The example displays the following output:
//       abacus
//       alabaster
//       atrium
//       any
//       actual
//       armoire
//       alarm
Dim input As String = "abacus -- alabaster - * - atrium -+- " +
                    "any -*- actual - + - armoir - - alarm"
Dim pattern As String = "\s-\s?[+*]?\s?-\s"
Dim elements() As String = Regex.Split(input, pattern)
For Each element In elements
    Console.WriteLine(element)
Next

' The example displays the following output:
'       abacus
'       alabaster
'       atrium
'       any
'       actual
'       armoir
'       alarm

El patrón de expresión regular \s-\s?[+*]?\s?-\s se define como se indica a continuación:

Patrón Descripción
\s- Coincide con un carácter de espacio en blanco seguido de un guion.
\s? Busca coincidencias con cero o un carácter de espacio en blanco.
[+*]? Coincide con cero o una aparición del carácter + o *.
\s? Busca coincidencias con cero o un carácter de espacio en blanco.
-\s Coincide con un guion seguido de un carácter de espacio en blanco.

Métodos String.IndexOf y String.Substring

Si no está interesado en todas las subcadenas de una cadena, puede que prefiera trabajar con uno de los métodos de comparación de cadenas que devuelve el índice en el que comienza la coincidencia. Después, puede llamar al método Substring para extraer la subcadena que desee. Entre los métodos de comparación de cadenas están los siguientes:

  • IndexOf, que devuelve el índice basado en cero de la primera aparición de un carácter o cadena en una instancia de cadena.

  • IndexOfAny, que devuelve el índice basado en cero de la instancia de cadena actual de la primera aparición de cualquier carácter de una matriz de caracteres.

  • LastIndexOf, que devuelve el índice basado en cero de la última aparición de un carácter o cadena en una instancia de cadena.

  • LastIndexOfAny, que devuelve un índice basado en cero en la instancia de la cadena actual de la última aparición de cualquier carácter de una matriz de caracteres.

En el ejemplo siguiente se utiliza el método IndexOf para encontrar los puntos de una cadena. A continuación, usa el método Substring para devolver oraciones completas.

String s = "This is the first sentence in a string. " +
               "More sentences will follow. For example, " +
               "this is the third sentence. This is the " +
               "fourth. And this is the fifth and final " +
               "sentence.";
var sentences = new List<String>();
int start = 0;
int position;

// Extract sentences from the string.
do
{
    position = s.IndexOf('.', start);
    if (position >= 0)
    {
        sentences.Add(s.Substring(start, position - start + 1).Trim());
        start = position + 1;
    }
} while (position > 0);

// Display the sentences.
foreach (var sentence in sentences)
    Console.WriteLine(sentence);

// The example displays the following output:
//       This is the first sentence in a string.
//       More sentences will follow.
//       For example, this is the third sentence.
//       This is the fourth.
//       And this is the fifth and final sentence.
    Dim input As String = "This is the first sentence in a string. " +
                        "More sentences will follow. For example, " +
                        "this is the third sentence. This is the " +
                        "fourth. And this is the fifth and final " +
                        "sentence."
    Dim sentences As New List(Of String)
    Dim start As Integer = 0
    Dim position As Integer

    ' Extract sentences from the string.
    Do
        position = input.IndexOf("."c, start)
        If position >= 0 Then
            sentences.Add(input.Substring(start, position - start + 1).Trim())
            start = position + 1
        End If
    Loop While position > 0

    ' Display the sentences.
    For Each sentence In sentences
        Console.WriteLine(sentence)
    Next
End Sub

' The example displays the following output:
'       This is the first sentence in a string.
'       More sentences will follow.
'       For example, this is the third sentence.
'       This is the fourth.
'       And this is the fifth and final sentence.

Vea también