Cadenas y literales de cadena

Una cadena es un objeto de tipo String cuyo valor es texto. Internamente, el texto se almacena como una colección secuencial de solo lectura de objetos Char. No hay ningún carácter que finalice en NULL al final de una cadena de C#; por lo tanto, la cadena de C# puede contener cualquier número de caracteres nulos insertados ("\0"). La propiedad Length de una cadena representa el número de objetos Char que contiene, no el número de caracteres Unicode. Para obtener acceso a los puntos de código Unicode individuales de una cadena, use el objeto StringInfo.

cadena frente System.String

En C#, la palabra clave string es un alias de String. Por lo tanto, String y string son equivalentes, aunque se recomienda usar el alias proporcionado string, ya que funciona incluso sin using System;. La clase String proporciona muchos métodos para crear, manipular y comparar cadenas de forma segura. Además, el lenguaje C# sobrecarga algunos operadores para simplificar las operaciones de cadena comunes. Para más información sobre la palabra clave, consulte string. Para obtener más información sobre el tipo y sus métodos, vea String.

Declaración e inicialización de cadenas

Puede declarar e inicializar cadenas de varias maneras, tal como se muestra en el ejemplo siguiente:

// Declare without initializing.
string message1;

// Initialize to null.
string message2 = null;

// Initialize as an empty string.
// Use the Empty constant instead of the literal "".
string message3 = System.String.Empty;

// Initialize with a regular string literal.
string oldPath = "c:\\Program Files\\Microsoft Visual Studio 8.0";

// Initialize with a verbatim string literal.
string newPath = @"c:\Program Files\Microsoft Visual Studio 9.0";

// Use System.String if you prefer.
System.String greeting = "Hello World!";

// In local variables (i.e. within a method body)
// you can use implicit typing.
var temp = "I'm still a strongly-typed System.String!";

// Use a const string to prevent 'message4' from
// being used to store another string value.
const string message4 = "You can't get rid of me!";

// Use the String constructor only when creating
// a string from a char*, char[], or sbyte*. See
// System.String documentation for details.
char[] letters = { 'A', 'B', 'C' };
string alphabet = new string(letters);

El operador new no se usa para crear un objeto de cadena, salvo cuando se inicialice la cadena con una matriz de caracteres.

Inicialice una cadena con el valor constante Empty para crear un objeto String cuya cadena tenga longitud cero. La representación literal de la cadena de una cadena de longitud cero es "". Mediante la inicialización de las cadenas con el valor Empty en lugar de null, puede reducir las posibilidades de que se produzca una excepción NullReferenceException. Use el método estático IsNullOrEmpty(String) para comprobar el valor de una cadena antes de intentar obtener acceso a ella.

Inmutabilidad de las cadenas

Los objetos de cadena son inmutables: no se pueden cambiar después de haberse creado. Todos los métodos String y operadores de C# que parecen modificar una cadena en realidad devuelven los resultados en un nuevo objeto de cadena. En el siguiente ejemplo, cuando el contenido de s1 y s2 se concatena para formar una sola cadena, las dos cadenas originales no se modifican. El operador += crea una nueva cadena que contiene el contenido combinado. Este nuevo objeto se asigna a la variable s1 y el objeto original que se asignó a s1 se libera para la recolección de elementos no utilizados porque ninguna otra variable contiene una referencia a él.

string s1 = "A string is more ";
string s2 = "than the sum of its chars.";

// Concatenate s1 and s2. This actually creates a new
// string object and stores it in s1, releasing the
// reference to the original object.
s1 += s2;

System.Console.WriteLine(s1);
// Output: A string is more than the sum of its chars.

Dado que una "modificación" de cadena es en realidad una creación de cadena, debe tener cuidado al crear referencias a las cadenas. Si crea una referencia a una cadena y después "modifica" la cadena original, la referencia seguirá apuntando al objeto original en lugar de al objeto nuevo creado al modificarse la cadena. El código siguiente muestra este comportamiento:

string str1 = "Hello ";
string str2 = str1;
str1 += "World";

System.Console.WriteLine(str2);
//Output: Hello

Para más información acerca de cómo crear cadenas nuevas basadas en modificaciones como las operaciones de buscar y reemplazar en la cadena original, consulte Modificación del contenido de cadenas.

Literales de cadena entre comillas

Los literales de cadena entre comillas comienzan y terminan con un solo carácter de comilla doble (") en la misma línea. Los literales de cadena entre comillas son más adecuados para las cadenas que caben en una sola línea y no incluyen ninguna secuencia de escape. Un literal de cadena entre comillas debe insertar caracteres de escape, como se muestra en el ejemplo siguiente:

string columns = "Column 1\tColumn 2\tColumn 3";
//Output: Column 1        Column 2        Column 3

string rows = "Row 1\r\nRow 2\r\nRow 3";
/* Output:
    Row 1
    Row 2
    Row 3
*/

string title = "\"The \u00C6olean Harp\", by Samuel Taylor Coleridge";
//Output: "The Æolean Harp", by Samuel Taylor Coleridge

Literales de cadenas literales

Los literales de cadena textual son más adecuados para cadenas de varias líneas, cadenas que contienen caracteres de barra diagonal inversa o comillas dobles insertadas. Las cadenas textuales conservan los caracteres de nueva línea como parte del texto de la cadena. Utilice comillas dobles para insertar una comilla simple dentro de una cadena textual. En el ejemplo siguiente se muestran algunos usos habituales de las cadenas textuales:

string filePath = @"C:\Users\scoleridge\Documents\";
//Output: C:\Users\scoleridge\Documents\

string text = @"My pensive SARA ! thy soft cheek reclined
    Thus on mine arm, most soothing sweet it is
    To sit beside our Cot,...";
/* Output:
My pensive SARA ! thy soft cheek reclined
    Thus on mine arm, most soothing sweet it is
    To sit beside our Cot,...
*/

string quote = @"Her name was ""Sara.""";
//Output: Her name was "Sara."

Literales de cadena sin formato

A partir de C# 11, puede usar literales de cadena sin formato para crear más fácilmente cadenas de varias líneas o usar cualquier carácter que requiera secuencias de escape. Los literales de cadena sin formato eliminan la necesidad de usar secuencias de escape. Puede escribir la cadena, incluido el formato de espacio en blanco, cómo quiera que aparezca en la salida. El literal de cadena sin formato:

  • Comienza y termina con una secuencia de al menos tres caracteres de comilla doble ("""). Se permite que más de tres caracteres consecutivos inicien y terminen la secuencia con el fin de admitir literales de cadena que contengan tres (o más) caracteres de comilla repetidos.
  • Los literales de cadena sin formato de una sola línea requieren los caracteres de comilla de apertura y cierre en la misma línea.
  • Los literales de cadena sin formato de varias líneas requieren que los caracteres de comilla de apertura y cierre estén en su propia línea.
  • En los literales de cadena sin formato de varias líneas, los espacios en blanco a la izquierda de las comillas de cierre se quitan.

En los ejemplos siguientes se muestran estas reglas:

string singleLine = """Friends say "hello" as they pass by.""";
string multiLine = """
    "Hello World!" is typically the first program someone writes.
    """;
string embeddedXML = """
       <element attr = "content">
           <body style="normal">
               Here is the main text
           </body>
           <footer>
               Excerpts from "An amazing story"
           </footer>
       </element >
       """;
// The line "<element attr = "content">" starts in the first column.
// All whitespace left of that column is removed from the string.

string rawStringLiteralDelimiter = """"
    Raw string literals are delimited 
    by a string of at least three double quotes,
    like this: """
    """";

En los ejemplos siguientes se muestran los errores del compilador notificados en función de estas reglas:

// CS8997: Unterminated raw string literal.
var multiLineStart = """This
    is the beginning of a string 
    """;

// CS9000: Raw string literal delimiter must be on its own line.
var multiLineEnd = """
    This is the beginning of a string """;

// CS8999: Line does not start with the same whitespace as the closing line
// of the raw string literal
var noOutdenting = """
    A line of text.
Trying to outdent the second line.
    """;

Los dos primeros ejemplos no son válidos porque los literales de cadena sin formato de varias líneas requieren la secuencia de comillas de apertura y cierre en su propia línea. El tercer ejemplo no es válido porque se anula la sangría del texto de la secuencia de comillas de cierre.

Al usar literales de cadena entre comillas o literales de cadena textuales, debe considerar los literales de cadena sin formato al generar texto que incluya caracteres que requieran secuencias de escape. Los literales de cadena sin formato serán más fáciles para todos, ya que se parecerán más al texto de salida. Por ejemplo, considere el código siguiente que incluye una cadena de JSON con formato:

string jsonString = """
{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Summary": "Hot",
  "DatesAvailable": [
    "2019-08-01T00:00:00-07:00",
    "2019-08-02T00:00:00-07:00"
  ],
  "TemperatureRanges": {
    "Cold": {
      "High": 20,
      "Low": -10
    },
    "Hot": {
      "High": 60,
      "Low": 20
    }
            },
  "SummaryWords": [
    "Cool",
    "Windy",
    "Humid"
  ]
}
""";

Compare ese texto con el texto equivalente de nuestro ejemplo de serialización JSON, que no usa esta nueva característica.

Secuencias de escape de cadena

Secuencia de escape Nombre de carácter Codificación Unicode
\' Comilla simple 0x0027
\" Comilla doble 0x0022
\\ Barra diagonal inversa 0x005C
\0 Null 0x0000
\a Alerta 0x0007
\b Retroceso 0x0008
\f Avance de página 0x000C
\n Nueva línea 0x000A
\r Retorno de carro 0x000D
\t Tabulación horizontal 0x0009
\v Tabulación vertical 0x000B
\u Secuencia de escape Unicode (UTF-16) \uHHHH (intervalo: 0000 - FFFF; ejemplo: \u00E7 = "ç")
\U Secuencia de escape Unicode (UTF-32) \U00HHHHHH (intervalo: 000000 - 10FFFF; ejemplo: \U0001F47D = "👽")
\x Secuencia de escape Unicode similar a "\u" excepto con longitud variable \xH[H][H][H] (intervalo: 0 - FFFF; ejemplo: \x00E7 o \x0E7 o \xE7 = "ç")

Advertencia

Cuando se usa la secuencia de escape \x y se especifican menos de 4 dígitos hexadecimales, si los caracteres que van inmediatamente después de la secuencia de escape son dígitos hexadecimales válidos (es decir, 0-9, A-f y a-f), se interpretará que forman parte de la secuencia de escape. Por ejemplo, \xA1 genera "¡", que es el punto de código U+00A1. Sin embargo, si el carácter siguiente es "A" o "a", la secuencia de escape se interpretará entonces como \xA1A y producirá "ਚ", que es el punto de código U+0A1A. En casos así, se pueden especificar los 4 dígitos hexadecimales (por ejemplo, \x00A1) para evitar posibles errores de interpretación.

Nota

En tiempo de compilación, las cadenas textuales se convierten en cadenas normales con las mismas secuencias de escape. Por lo tanto, si se muestra una cadena textual en la ventana Inspección del depurador, verá los caracteres de escape agregados por el compilador, no la versión textual del código fuente. Por ejemplo, la cadena textual @"C:\files.txt" aparecerá en la ventana de inspección como "C:\files.txt".

Cadenas de formato

Una cadena de formato es una cadena cuyo contenido se determina de manera dinámica en tiempo de ejecución. Las cadenas de formato se crean mediante la inserción de expresiones interpoladas o marcadores de posición entre llaves dentro de una cadena. Todo lo incluido entre llaves ({...}) se resolverá en un valor y se generará como una cadena con formato en tiempo de ejecución. Existen dos métodos para crear cadenas de formato: interpolación de cadenas y formato compuesto.

Interpolación de cadenas

Disponible en C# 6.0 y versiones posteriores, las cadenas interpoladas se identifican por el carácter especial $ e incluyen expresiones interpoladas entre llaves. Si no está familiarizado con la interpolación de cadenas, consulte el tutorial interactivo Interpolación de cadenas en C# para obtener información general rápidamente.

Use la interpolación de cadenas para mejorar la legibilidad y el mantenimiento del código. Con la interpolación de cadenas se obtienen los mismos resultados que con el método String.Format, pero mejora la facilidad de uso y la claridad en línea.

var jh = (firstName: "Jupiter", lastName: "Hammon", born: 1711, published: 1761);
Console.WriteLine($"{jh.firstName} {jh.lastName} was an African American poet born in {jh.born}.");
Console.WriteLine($"He was first published in {jh.published} at the age of {jh.published - jh.born}.");
Console.WriteLine($"He'd be over {Math.Round((2018d - jh.born) / 100d) * 100d} years old today.");

// Output:
// Jupiter Hammon was an African American poet born in 1711.
// He was first published in 1761 at the age of 50.
// He'd be over 300 years old today.

A partir de C# 10, se puede utilizar la interpolación de cadenas para inicializar una cadena constante cuando todas las expresiones utilizadas para los marcadores de posición son también cadenas constantes.

A partir de C# 11, puede combinar literales de cadena sin formato con interpolaciones de cadenas. La cadena de formato se inicia y termina con tres o más comillas dobles sucesivas. Si la cadena de salida debe contener el carácter { o }, puede usar caracteres $ adicionales para especificar cuántos caracteres { y } comienzan y terminan una interpolación. Todas las secuencias de menos caracteres { o } se incluye en la salida. En el ejemplo siguiente se muestra cómo puede usar esa característica para mostrar la distancia de un punto desde el origen y colocar el punto entre llaves:

int X = 2;
int Y = 3;

var pointMessage = $$"""The point {{{X}}, {{Y}}} is {{Math.Sqrt(X * X + Y * Y)}} from the origin.""";

Console.WriteLine(pointMessage);
// Output:
// The point {2, 3} is 3.605551275463989 from the origin.

Formatos compuestos

String.Format emplea marcadores de posición entre llaves para crear una cadena de formato. Los resultados de este ejemplo son similares a la salida del método de interpolación de cadenas usado anteriormente.

var pw = (firstName: "Phillis", lastName: "Wheatley", born: 1753, published: 1773);
Console.WriteLine("{0} {1} was an African American poet born in {2}.", pw.firstName, pw.lastName, pw.born);
Console.WriteLine("She was first published in {0} at the age of {1}.", pw.published, pw.published - pw.born);
Console.WriteLine("She'd be over {0} years old today.", Math.Round((2018d - pw.born) / 100d) * 100d);

// Output:
// Phillis Wheatley was an African American poet born in 1753.
// She was first published in 1773 at the age of 20.
// She'd be over 300 years old today.

Para más información sobre cómo dar formato a los tipos .NET, consulte Aplicación de formato a tipos en .NET.

Subcadenas

Una subcadena es cualquier secuencia de caracteres que se encuentra en una cadena. Use el método Substring para crear una nueva cadena de una parte de la cadena original. Puede buscar una o más apariciones de una subcadena con el método IndexOf. Use el método Replace para reemplazar todas las apariciones de una subcadena especificada por una nueva cadena. Al igual que el método Substring, Replace devuelve en realidad una cadena nueva y no modifica la cadena original. Para más información, consulte Cómo: Buscar cadenas y Procedimiento para modificar el contenido de cadenas.

string s3 = "Visual C# Express";
System.Console.WriteLine(s3.Substring(7, 2));
// Output: "C#"

System.Console.WriteLine(s3.Replace("C#", "Basic"));
// Output: "Visual Basic Express"

// Index values are zero-based
int index = s3.IndexOf("C");
// index = 7

Acceso a caracteres individuales

Puede utilizar la notación de matriz con un valor de índice para adquirir acceso de solo lectura a caracteres individuales, como en el ejemplo siguiente:

string s5 = "Printing backwards";

for (int i = 0; i < s5.Length; i++)
{
    System.Console.Write(s5[s5.Length - i - 1]);
}
// Output: "sdrawkcab gnitnirP"

Si el método String no proporciona la funcionalidad que debe tener para modificar los caracteres individuales de una cadena, puede usar un objeto StringBuilder para modificar los caracteres individuales "en contexto" y, después, crear una cadena para almacenar los resultados mediante el método StringBuilder. En el ejemplo siguiente, se supone que debe modificar la cadena original de una manera determinada y, después, almacenar los resultados para un uso futuro:

string question = "hOW DOES mICROSOFT wORD DEAL WITH THE cAPS lOCK KEY?";
System.Text.StringBuilder sb = new System.Text.StringBuilder(question);

for (int j = 0; j < sb.Length; j++)
{
    if (System.Char.IsLower(sb[j]) == true)
        sb[j] = System.Char.ToUpper(sb[j]);
    else if (System.Char.IsUpper(sb[j]) == true)
        sb[j] = System.Char.ToLower(sb[j]);
}
// Store the new string.
string corrected = sb.ToString();
System.Console.WriteLine(corrected);
// Output: How does Microsoft Word deal with the Caps Lock key?

Cadenas nulas y cadenas vacías

Una cadena vacía es una instancia de un objeto System.String que contiene cero caracteres. Las cadenas vacías se utilizan a menudo en distintos escenarios de programación para representar un campo de texto en blanco. Puede llamar a métodos en cadenas vacías porque son objetos System.String válidos. Las cadenas vacías se inicializan como sigue:

string s = String.Empty;

En cambio, una cadena nula no hace referencia a una instancia de un objeto System.String y cualquier intento de llamar a un método en una cadena nula produce una excepción NullReferenceException. Sin embargo, puede utilizar cadenas nulas en operaciones de comparación y concatenación con otras cadenas. En los ejemplos siguientes se muestran algunos casos en que una referencia a una cadena nula provoca y no provoca una excepción:

string str = "hello";
string nullStr = null;
string emptyStr = String.Empty;

string tempStr = str + nullStr;
// Output of the following line: hello
Console.WriteLine(tempStr);

bool b = (emptyStr == nullStr);
// Output of the following line: False
Console.WriteLine(b);

// The following line creates a new empty string.
string newStr = emptyStr + nullStr;

// Null strings and empty strings behave differently. The following
// two lines display 0.
Console.WriteLine(emptyStr.Length);
Console.WriteLine(newStr.Length);
// The following line raises a NullReferenceException.
//Console.WriteLine(nullStr.Length);

// The null character can be displayed and counted, like other chars.
string s1 = "\x0" + "abc";
string s2 = "abc" + "\x0";
// Output of the following line: * abc*
Console.WriteLine("*" + s1 + "*");
// Output of the following line: *abc *
Console.WriteLine("*" + s2 + "*");
// Output of the following line: 4
Console.WriteLine(s2.Length);

Uso de stringBuilder para la creación rápida de cadenas

Las operaciones de cadena en .NET están muy optimizadas y en la mayoría de los casos no afectan significativamente al rendimiento. Sin embargo, en algunos escenarios, como los bucles de pequeñas dimensiones que se ejecutan cientos o miles de veces, las operaciones de cadena pueden afectar al rendimiento. La clase StringBuilder crea un búfer de cadena que proporciona un mejor rendimiento si el programa realiza muchas manipulaciones de cadenas. La cadena StringBuilder también permite reasignar caracteres individuales, algo que el tipo de datos de cadena integrado no admite. Por ejemplo, este código cambia el contenido de una cadena sin crear una nueva:

System.Text.StringBuilder sb = new System.Text.StringBuilder("Rat: the ideal pet");
sb[0] = 'C';
System.Console.WriteLine(sb.ToString());
//Outputs Cat: the ideal pet

En este ejemplo, se usa un objeto StringBuilder para crear una cadena a partir de un conjunto de tipos numéricos:

var sb = new StringBuilder();

// Create a string composed of numbers 0 - 9
for (int i = 0; i < 10; i++)
{
    sb.Append(i.ToString());
}
Console.WriteLine(sb);  // displays 0123456789

// Copy one character of the string (not possible with a System.String)
sb[0] = sb[9];

Console.WriteLine(sb);  // displays 9123456789

Cadenas, métodos de extensión y LINQ

Dado que el tipo String implementa IEnumerable<T>, puede usar los métodos de extensión definidos en la clase Enumerable en cadenas. Para evitar el desorden visual, estos métodos se excluyen de IntelliSense para el tipo String, pero aun así están disponibles. También puede usar expresiones de consulta LINQ en cadenas. Para más información, consulte LINQ y cadenas.