Substring devuelve un string nuevo formado por una parte de otro existente. Tiene dos sobrecargas:
s.Substring(startIndex): desdestartIndexhasta el final.s.Substring(startIndex, length):lengthcaracteres, empezando enstartIndex.
Los índices empiezan en 0, y el segundo argumento es una longitud, no una posición final. Esa es la diferencia con substring(start, end) de JavaScript y substring(begin, end) de Java, y el origen de muchos errores de desfase cuando se porta código.
Salida:
4271-BLK
SHOE
4271
BLK
0
SHOE-4271-BLK
Como todos los métodos de string, Substring no modifica el original; devuelve un string nuevo, así que asigna el resultado.
ArgumentOutOfRangeException y cómo evitarla
Substring lanza ArgumentOutOfRangeException cuando el rango pedido no cabe dentro del string: un startIndex negativo, un startIndex mayor que Length, un length negativo, o startIndex + length mayor que Length. No hay un recorte silencioso, a diferencia de los slices de Python.
Salida:
Substring(2, 4) -> "ffee"
Substring(2, 5) -> ArgumentOutOfRangeException
Substring(6) -> ""
Substring(7) -> ArgumentOutOfRangeException
Substring(-1) -> ArgumentOutOfRangeException
Substring(0, 10) -> ArgumentOutOfRangeException
La protección habitual es limitar la longitud a lo que queda del string con Math.Min:
Salida:
ffee
ea
[]
[]
Llamar a Substring sobre un string null lanza NullReferenceException, no ArgumentOutOfRangeException, y por eso el método auxiliar comprueba primero si es null.
Los n primeros y últimos caracteres
Tomar un prefijo o un sufijo es el uso más común de Substring. Los dos lanzan una excepción cuando el string es más corto que n, así que el código de producción comprueba la longitud:
Salida:
**** 1111
An Unexpecte...
Hi
Hi
Cuando lo que quieres descartar es la primera parte del string, basta con Substring(n): "#FF8800".Substring(1) es "FF8800". El método relacionado Remove(start, count) hace lo contrario que Substring: devuelve el string sin ese rango.
Substring con IndexOf
La mayoría de los substrings reales se encuentran, no se cuentan. IndexOf devuelve la posición de un carácter o de un string (o -1 si no está), y LastIndexOf busca desde el final. Combínalos con Substring para cortar texto alrededor de un separador:
Salida:
maya.lopez
example.com
pdf
report.final.v2
WARN
Disk almost full
La aritmética sigue una regla: para tomar el texto entre las posiciones a y b (sin incluir ninguna de las dos), el inicio es a + 1 y la longitud es b - a - 1.
El peligro es que IndexOf devuelva -1. email.Substring(0, -1) lanza una excepción y, peor aún, email.Substring(-1 + 1) devuelve en silencio el string entero. Compruébalo siempre:
Salida:
shop.io
(none)
Split como alternativa
Cuando un string es una lista de campos con un separador, Split es más sencillo que una cadena de llamadas a IndexOf y Substring, y no tiene aritmética de índices con la que equivocarse:
Salida:
Ana Silva
Porto
maya.lopez
beach.jpg
Split reserva un array más un string por campo, así que para un único corte dentro de un bucle crítico IndexOf y Substring son más baratos. En todo lo demás, gana la legibilidad. Hay más opciones (varios separadores, quitar entradas vacías, un número máximo) en métodos de string. Para patrones como "los dígitos que van después de ID:", una expresión regular suele ser la herramienta más clara.
El operador de rango (C# 8 y posteriores)
C# 8 añadió la sintaxis de índices y rangos, que funciona con strings y arrays. ^n significa "n desde el final" y a..b es el rango desde a hasta b sin incluir b, así que el segundo número es un índice final, no una longitud:
// C# 8 and later
string sku = "SHOE-4271-BLK";
string first4 = sku[..4]; // "SHOE" same as Substring(0, 4)
string middle = sku[5..9]; // "4271" same as Substring(5, 4)
string last3 = sku[^3..]; // "BLK" same as Substring(sku.Length - 3)
string noEnds = sku[1..^1]; // "HOE-4271-BL" drop first and last
char lastChar = sku[^1]; // 'K'
En un string, un rango se compila como una llamada a Substring, así que reserva un string nuevo y lanza ArgumentOutOfRangeException en los mismos casos fuera de límites. Cuál usar es cuestión de estilo; los rangos se leen mejor cuando los dos extremos se miden desde lados distintos ([1..^1]).
Substrings sin copiar: Span
Cada llamada a Substring reserva un string nuevo. En código de parseo sensible al rendimiento (leer archivos grandes, atender muchas peticiones), .NET Core 2.1 y posteriores ofrecen ReadOnlySpan<char>, una vista de una parte del string original sin copia:
ReadOnlySpan<char> line = "2026-09-24,42.50,EUR".AsSpan();
ReadOnlySpan<char> amount = line.Slice(11, 5); // "42.50", no allocation
decimal value = decimal.Parse(amount, provider: CultureInfo.InvariantCulture);
Los spans no pueden guardarse en campos de clases normales ni usarse a través de un await, así que su sitio es el código síncrono y ajustado. En el código de aplicación habitual, Substring es la herramienta adecuada.
Errores comunes
- Pasar un índice final como longitud.
s.Substring(2, 5)significa cinco caracteres desde el índice 2, no "del índice 2 al 5". Para tomar el texto desde el índiceahasta elbsin incluirlo, la longitud esb - a. - No comprobar si
IndexOfdevuelve -1. O una excepción o, con+ 1, el string entero por accidente. - Suponer que los strings cortos son lo bastante largos. Un nombre, un código o una línea de un archivo pueden ser más cortos de lo que esperas. Limita con
Math.Mino compruebaLength. - Descartar el resultado.
s.Substring(1);en su propia línea no hace nada útil.
Preguntas frecuentes
¿Cómo funciona Substring en C#?
s.Substring(start) devuelve los caracteres desde el índice start hasta el final; s.Substring(start, length) devuelve length caracteres empezando en start. Los índices empiezan en 0, y el segundo argumento es una longitud, no un índice final: "Hello".Substring(1, 3) es "ell". El string original no cambia.
¿Por qué Substring lanza ArgumentOutOfRangeException?
Porque start es negativo o mayor que la longitud del string, o porque start + length se pasa del final. La causa más común es una longitud calculada para un string más largo, o un resultado -1 de IndexOf usado como inicio. Limita la longitud con Math.Min(length, s.Length - start) y comprueba los resultados de IndexOf antes de usarlos.
¿Cómo obtengo los últimos n caracteres de un string en C#?
s.Substring(s.Length - n) devuelve los últimos n caracteres, siempre que n <= s.Length. Para no fallar con strings cortos, usa s.Length <= n ? s : s.Substring(s.Length - n). En C# 8 y posteriores también puedes escribir s[^n..], que lanza la misma excepción cuando n es demasiado grande.
¿Cómo obtengo el texto que hay entre dos caracteres en C#?
Encuentra las dos posiciones con IndexOf y toma la parte que hay entre ellas: int start = s.IndexOf('(') + 1; int end = s.IndexOf(')', start); string inside = s.Substring(start, end - start);. Comprueba que cada IndexOf encontró algo (no -1) antes de llamar a Substring. Para patrones complejos, una expresión regular es más clara.
¿Empieza Substring en cero en C#?
Sí. El primer carácter está en el índice 0 y el último en Length - 1. Substring(0, 3) devuelve los tres primeros caracteres. Pasar el propio Length como inicio está permitido y devuelve un string vacío.