Tengo esta cadena: ABCDEFGHIJ
Necesito reemplazar de la posición 4 a la posición 5 con la cuerda ZX
Se verá así: ABCZXFGHIJ
Pero no usar con string.replace("DE","ZX")
- Necesito usar con posición
¿Cómo puedo hacerlo?
Tengo esta cadena: ABCDEFGHIJ
Necesito reemplazar de la posición 4 a la posición 5 con la cuerda ZX
Se verá así: ABCZXFGHIJ
Pero no usar con string.replace("DE","ZX")
- Necesito usar con posición
¿Cómo puedo hacerlo?
Respuestas:
La forma más fácil de agregar y eliminar rangos en una cadena es usar el StringBuilder
.
var theString = "ABCDEFGHIJ";
var aStringBuilder = new StringBuilder(theString);
aStringBuilder.Remove(3, 2);
aStringBuilder.Insert(3, "ZX");
theString = aStringBuilder.ToString();
Una alternativa es usarla String.Substring
, pero creo que el StringBuilder
código se vuelve más legible.
string s = "ABCDEFGH";
s= s.Remove(3, 2).Insert(3, "ZX");
Aquí hay un método de extensión que no usa StringBuilder o Substring. Este método también permite que la cadena de reemplazo se extienda más allá de la longitud de la cadena de origen.
//// str - the source string
//// index- the start location to replace at (0-based)
//// length - the number of characters to be removed before inserting
//// replace - the string that is replacing characters
public static string ReplaceAt(this string str, int index, int length, string replace)
{
return str.Remove(index, Math.Min(length, str.Length - index))
.Insert(index, replace);
}
Cuando utilice esta función, si desea que toda la cadena de reemplazo reemplace tantos caracteres como sea posible, configure la longitud a la longitud de la cadena de reemplazo:
"0123456789".ReplaceAt(7, 5, "Hello") = "0123456Hello"
De lo contrario, puede especificar la cantidad de caracteres que se eliminarán:
"0123456789".ReplaceAt(2, 2, "Hello") = "01Hello456789"
Si especifica que la longitud sea 0, esta función actúa igual que la función de inserción:
"0123456789".ReplaceAt(4, 0, "Hello") = "0123Hello456789"
Supongo que esto es más eficiente ya que la clase StringBuilder no necesita inicializarse y ya que usa operaciones más básicas. Por favor, corríjame si estoy equivocado. :)
Use String.Substring()
(detalles aquí ) para cortar la parte izquierda, luego su reemplazo, luego la parte derecha. Juega con índices hasta que lo hagas bien :)
Algo como:
string replacement=original.Substring(0,start)+
rep+original.Substring(start+rep.Length);
Como un método de extensión.
public static class StringBuilderExtension
{
public static string SubsituteString(this string OriginalStr, int index, int length, string SubsituteStr)
{
return new StringBuilder(OriginalStr).Remove(index, length).Insert(index, SubsituteStr).ToString();
}
}
string s = "ABCDEFG";
string t = "st";
s = s.Remove(4, t.Length);
s = s.Insert(4, t);
t
nuevo.
Si le importa el rendimiento, lo que quiere evitar aquí son las asignaciones. Y si está utilizando .Net Core 2.1+ (o, aún no publicado, .Net Standard 2.1), puede hacerlo utilizando el string.Create
método :
public static string ReplaceAt(this string str, int index, int length, string replace)
{
return string.Create(str.Length - length + replace.Length, (str, index, length, replace),
(span, state) =>
{
state.str.AsSpan().Slice(0, state.index).CopyTo(span);
state.replace.AsSpan().CopyTo(span.Slice(state.index));
state.str.AsSpan().Slice(state.index + state.length).CopyTo(span.Slice(state.index + state.replace.Length));
});
}
Este enfoque es más difícil de entender que las alternativas, pero es el único que asignará solo un objeto por llamada: la cadena recién creada.
Como otros han mencionado, la Substring()
función está ahí por una razón:
static void Main(string[] args)
{
string input = "ABCDEFGHIJ";
string output = input.Overwrite(3, "ZX"); // 4th position has index 3
// ABCZXFGHIJ
}
public static string Overwrite(this string text, int position, string new_text)
{
return text.Substring(0, position) + new_text + text.Substring(position + new_text.Length);
}
También calculé esto contra la StringBuilder
solución y obtuve 900 tics frente a 875. Por lo tanto, es un poco más lento.
"ABCDEFGHIJ"
y "ZX"
son de diferentes longitudes.
Aún otra
public static string ReplaceAtPosition(this string self, int position, string newValue)
{
return self.Remove(position, newValue.Length).Insert(position, newValue);
}
Con la ayuda de esta publicación, creo la siguiente función con verificaciones de longitud adicionales
public string ReplaceStringByIndex(string original, string replaceWith, int replaceIndex)
{
if (original.Length >= (replaceIndex + replaceWith.Length))
{
StringBuilder rev = new StringBuilder(original);
rev.Remove(replaceIndex, replaceWith.Length);
rev.Insert(replaceIndex, replaceWith);
return rev.ToString();
}
else
{
throw new Exception("Wrong lengths for the operation");
}
}
Todas las demás respuestas no funcionan si la cadena contiene caracteres Unicode (como Emojis) porque un carácter Unicode pesa más bytes que un carácter.
Ejemplo : el emoji '🎶' convertido a bytes, pesará el equivalente a 2 caracteres. Entonces, si el carácter unicode se coloca al comienzo de su cadena, el
offset
parámetro se desplazará).
Con este tema , extiendo la clase StringInfo a Reemplazar por posición manteniendo el algoritmo de Nick Miller para evitar eso:
public static class StringInfoUtils
{
public static string ReplaceByPosition(this string str, string replaceBy, int offset, int count)
{
return new StringInfo(str).ReplaceByPosition(replaceBy, offset, count).String;
}
public static StringInfo ReplaceByPosition(this StringInfo str, string replaceBy, int offset, int count)
{
return str.RemoveByTextElements(offset, count).InsertByTextElements(offset, replaceBy);
}
public static StringInfo RemoveByTextElements(this StringInfo str, int offset, int count)
{
return new StringInfo(string.Concat(
str.SubstringByTextElements(0, offset),
offset + count < str.LengthInTextElements
? str.SubstringByTextElements(offset + count, str.LengthInTextElements - count - offset)
: ""
));
}
public static StringInfo InsertByTextElements(this StringInfo str, int offset, string insertStr)
{
if (string.IsNullOrEmpty(str?.String))
return new StringInfo(insertStr);
return new StringInfo(string.Concat(
str.SubstringByTextElements(0, offset),
insertStr,
str.LengthInTextElements - offset > 0 ? str.SubstringByTextElements(offset, str.LengthInTextElements - offset) : ""
));
}
}
Estaba buscando una solución con los siguientes requisitos:
La solución que mejor me conviene es esta:
// replace `oldString[i]` with `c`
string newString = new StringBuilder(oldString).Replace(oldString[i], c, i, 1).ToString();
Esto usa StringBuilder.Replace(oldChar, newChar, position, count)
La otra solución que satisface mis requisitos es usar Substring
con concatenación:
string newString = oldStr.Substring(0, i) + c + oldString.Substring(i+1, oldString.Length);
Esto también está bien. me conjetura que no es tan eficiente como la primera actuación sabia (debido a la concatenación de cadenas innecesario). Pero la optimización prematura es la raíz de todo mal .
Así que elige el que más te guste :)
Es mejor usar el String.substr()
.
Me gusta esto:
ReplString = GivenStr.substr(0, PostostarRelStr)
+ GivenStr(PostostarRelStr, ReplString.lenght());
string myString = "ABCDEFGHIJ";
string modifiedString = new StringBuilder(myString){[3]='Z', [4]='X'}.ToString();
Déjame explicarte mi solución.
Dado el enunciado del problema de alterar una cadena en sus dos posiciones específicas (“posición 4 a posición 5”) con dos caracteres 'Z' y 'X' y la pregunta es usar el índice de posición para alterar la cadena y no la cadena Reemplazar ( ) método (puede deberse a la posibilidad de repetición de algunos caracteres en la cadena real), preferiría utilizar un enfoque minimalista para lograr el objetivo sobre el uso de Substring()
y cadena Concat()
o cadena Remove()
y Insert()
enfoque. Aunque todas esas soluciones servirán para lograr el mismo objetivo, pero solo depende de la elección personal y la filosofía de resolver con un enfoque minimalista.
Volviendo a la solución mencionada anteriormente, si miramos más de cercastring
yStringBuilder
, ambos tratan internamente una cadena dada como una matriz de caracteres. Si observamos su implementación StringBuilder
, mantiene una variable interna algo así como " internal char[] m_ChunkChars;
" para capturar la cadena dada. Ahora, dado que esta es una variable interna, no podemos acceder directamente a la misma. Para el mundo externo, para poder acceder y modificar esa matriz de caracteres, los StringBuilder
expone a través de la propiedad del indexador que se parece a continuación
[IndexerName("Chars")]
public char this[int index]
{
get
{
StringBuilder stringBuilder = this;
do
{
// … some code
return stringBuilder.m_ChunkChars[index1];
// … some more code
}
}
set
{
StringBuilder stringBuilder = this;
do
{
//… some code
stringBuilder.m_ChunkChars[index1] = value;
return;
// …. Some more code
}
}
}
Mi solución mencionada anteriormente aprovecha esta capacidad de indexador para alterar directamente la matriz de caracteres mantenida internamente, que IMO es eficiente y minimalista.
Por cierto; podemos reescribir la solución anterior de manera más elaborada, algo como a continuación
string myString = "ABCDEFGHIJ";
StringBuilder tempString = new StringBuilder(myString);
tempString[3] = 'Z';
tempString[4] = 'X';
string modifiedString = tempString.ToString();
En este contexto, también me gustaría mencionar que en caso de string
que también tenga una propiedad de indexador como un medio para exponer su matriz de caracteres interna, pero en este caso solo tiene la propiedad Getter (y no Setter) ya que la cadena es inmutable por naturaleza. Y es por eso que necesitamos usar StringBuilder
para alterar la matriz de caracteres.
[IndexerName("Chars")]
public extern char this[int index] { [SecuritySafeCritical, __DynamicallyInvokable, MethodImpl(MethodImplOptions.InternalCall)] get; }
Y por último, pero no menos importante, esta solución solo se ajusta mejor a este problema específico en el que la solicitud es reemplazar solo unos pocos caracteres con un índice de posición conocido por adelantado. Es posible que no sea la mejor opción cuando el requisito es alterar una cadena bastante larga, es decir, el número de caracteres a modificar es grande en números.
String timestamp = "2019-09-18 21.42.05.000705";
String sub1 = timestamp.substring(0, 19).replace('.', ':');
String sub2 = timestamp.substring(19, timestamp.length());
System.out.println("Original String "+ timestamp);
System.out.println("Replaced Value "+ sub1+sub2);
Aquí hay un método de extensión simple:
public static class StringBuilderExtensions
{
public static StringBuilder Replace(this StringBuilder sb, int position, string newString)
=> sb.Replace(position, newString.Length, newString);
public static StringBuilder Replace(this StringBuilder sb, int position, int length, string newString)
=> (newString.Length <= length)
? sb.Remove(position, newString.Length).Insert(position, newString)
: sb.Remove(position, length).Insert(position, newString.Substring(0, length));
}
Úselo así:
var theString = new string(' ', 10);
var sb = new StringBuilder(theString);
sb.Replace(5, "foo");
return sb.ToString();
Creo que la forma más simple sería esta: (sin stringbuilder)
string myString = "ABCDEFGHIJ";
char[] replacementChars = {'Z', 'X'};
byte j = 0;
for (byte i = 3; i <= 4; i++, j++)
{
myString = myString.Replace(myString[i], replacementChars[j]);
}
Esto funciona porque una variable de tipo cadena se puede tratar como una matriz de variables char. Puede, por ejemplo, referirse al segundo carácter de una variable de cadena con el nombre "myString" como myString [1]
string
los caracteres a ser reemplazados aparecen una sola vez. Si ejecuta esto contra, digamos, "DBCDEFGHIE"
entonces obtiene "ZBCZXFGHIX"
, que no es el resultado deseado. Además, no estaría de acuerdo en que esto sea más simple que las otras StringBuilder
soluciones que se publicaron hace dos años y medio.