La forma más eficiente de eliminar caracteres especiales de la cadena


266

Quiero eliminar todos los caracteres especiales de una cadena. Los caracteres permitidos son AZ (mayúsculas o minúsculas), números (0-9), guión bajo (_) o el signo de punto (.).

Tengo lo siguiente, funciona pero sospecho (¡lo sé!) No es muy eficiente:

    public static string RemoveSpecialCharacters(string str)
    {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < str.Length; i++)
        {
            if ((str[i] >= '0' && str[i] <= '9')
                || (str[i] >= 'A' && str[i] <= 'z'
                    || (str[i] == '.' || str[i] == '_')))
                {
                    sb.Append(str[i]);
                }
        }

        return sb.ToString();
    }

¿Cuál es la forma más eficiente de hacer esto? ¿Cómo se vería una expresión regular y cómo se compara con la manipulación normal de cadenas?

Las cadenas que se limpiarán serán bastante cortas, generalmente de entre 10 y 30 caracteres de longitud.


55
No pondré esto en una respuesta, ya que no será más eficiente, pero hay una serie de métodos de char estáticos como char.IsLetterOrDigit () que podría usar en su declaración if para que al menos sea más legible.
Martin Harris

55
No estoy seguro de que verificar A a z sea seguro, ya que trae 6 caracteres que no son alfabéticos, solo uno de los cuales se desea (barra inferior).
Steven Sudit

44
Concéntrese en hacer que su código sea más legible. a menos que esté haciendo esto en un ciclo como 500 veces por segundo, la eficiencia no es gran cosa. Use una expresión regular y será mucho más fácil de leer.l
Byron Whitlock el

44
Byron, probablemente tengas razón sobre la necesidad de enfatizar la legibilidad. Sin embargo, soy escéptico acerca de que regexp sea legible. :-)
Steven Sudit

2
Las expresiones regulares son legibles o no, es como si el alemán fuera legible o no; depende de si lo sabes o no (aunque en ambos casos te encontrarás de vez en cuando con reglas gramaticales que no tienen sentido;)
Blixt

Respuestas:


325

¿Por qué crees que tu método no es eficiente? En realidad, es una de las formas más eficientes en que puede hacerlo.

Por supuesto, debe leer el carácter en una variable local o usar un enumerador para reducir el número de accesos a la matriz:

public static string RemoveSpecialCharacters(this string str) {
   StringBuilder sb = new StringBuilder();
   foreach (char c in str) {
      if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '.' || c == '_') {
         sb.Append(c);
      }
   }
   return sb.ToString();
}

Una cosa que hace que un método como este sea eficiente es que escala bien. El tiempo de ejecución será relativo a la longitud de la cadena. No hay sorpresas desagradables si lo usarías en una cadena grande.

Editar:
Hice una prueba de rendimiento rápida, ejecutando cada función un millón de veces con una cadena de 24 caracteres. Estos son los resultados:

Función original: 54,5 ms.
Mi cambio sugerido: 47.1 ms.
Mina con capacidad de StringBuilder configurada: 43.3 ms.
Expresión regular: 294,4 ms.

Edición 2: agregué la distinción entre AZ y az en el código anterior. (Volví a realizar la prueba de rendimiento y no hay una diferencia notable).

Edición 3:
probé la solución lookup + char [], y se ejecuta en unos 13 ms.

El precio a pagar es, por supuesto, la inicialización de la gran tabla de búsqueda y mantenerla en la memoria. Bueno, no son tantos datos, pero son muchos para una función tan trivial ...

private static bool[] _lookup;

static Program() {
   _lookup = new bool[65536];
   for (char c = '0'; c <= '9'; c++) _lookup[c] = true;
   for (char c = 'A'; c <= 'Z'; c++) _lookup[c] = true;
   for (char c = 'a'; c <= 'z'; c++) _lookup[c] = true;
   _lookup['.'] = true;
   _lookup['_'] = true;
}

public static string RemoveSpecialCharacters(string str) {
   char[] buffer = new char[str.Length];
   int index = 0;
   foreach (char c in str) {
      if (_lookup[c]) {
         buffer[index] = c;
         index++;
      }
   }
   return new string(buffer, 0, index);
}

44
Estoy de acuerdo. El único otro cambio que haría es agregar el argumento de capacidad inicial al constructor StringBuilder, "= new StringBuilder (str.Length)".
David

2
Mi respuesta, usando un char[]búfer en lugar de StringBuilder, tiene una ligera ventaja en este según mis pruebas. (Sin embargo, el mío es menos legible, por lo que el pequeño beneficio de rendimiento probablemente no valga la pena.)
LukeH

1
@ Steven: Ese puede ser el caso, ¡pero los puntos de referencia hablan por sí mismos! En mis pruebas, el uso de un char[]búfer funciona (ligeramente) mejor que StringBuilder, incluso cuando se escala a cadenas que tienen decenas de miles de caracteres de longitud.
LukeH

10
@downvoter: ¿Por qué el downvote? Si no explica lo que cree que está mal, no puede mejorar la respuesta.
Guffa

2
@SILENT: No, no lo hace, pero solo debes hacerlo una vez. Si asigna una matriz tan grande cada vez que llama al método (y si llama al método con frecuencia), el método se convierte en el más lento y causa mucho trabajo para el recolector de basura.
Guffa

195

Bueno, a menos que realmente necesite exprimir el rendimiento de su función, solo elija lo que sea más fácil de mantener y comprender. Una expresión regular se vería así:

Para un rendimiento adicional, puede precompilarlo o simplemente decirle que se compile en la primera llamada (las llamadas posteriores serán más rápidas).

public static string RemoveSpecialCharacters(string str)
{
    return Regex.Replace(str, "[^a-zA-Z0-9_.]+", "", RegexOptions.Compiled);
}

1
Supongo que esta es probablemente una consulta lo suficientemente compleja como para que sea más rápida que el enfoque del OP, especialmente si se compila previamente. Sin embargo, no tengo evidencia que lo respalde. Debería ser probado. A menos que sea drásticamente más lento, elegiría este enfoque independientemente, ya que es mucho más fácil de leer y mantener. +1
rmeador

66
Es una expresión regular muy simple (sin retroceso o cualquier cosa compleja allí), por lo que debería ser bastante rápido.

9
@rmeador: sin ser compilado es aproximadamente 5 veces más lento, compilado es 3 veces más lento que su método. Todavía 10 veces más simple :-D
user7116

66
Las expresiones regulares no son martillos mágicos y nunca son más rápidas que el código optimizado a mano.
Christian Klauser

2
Para aquellos que recuerdan la famosa cita de Knuth sobre la optimización, aquí es donde comenzar. Luego, si descubres que necesitas el rendimiento adicional de la milésima de milisegundo, utiliza una de las otras técnicas.
John

15

Sugiero crear una tabla de búsqueda simple, que puede inicializar en el constructor estático para establecer cualquier combinación de caracteres como válida. Esto le permite hacer una verificación rápida y única.

editar

Además, para la velocidad, querrás inicializar la capacidad de tu StringBuilder a la longitud de tu cadena de entrada. Esto evitará reasignaciones. Estos dos métodos juntos le darán velocidad y flexibilidad.

otra edición

Creo que el compilador podría optimizarlo, pero por cuestiones de estilo y eficiencia, recomiendo foreach en lugar de por.


Para matrices, fory foreachproducir código similar. Sin embargo, no sé sobre cuerdas. Dudo que el JIT sepa sobre la naturaleza de tipo cadena de String.
Christian Klauser

1
Apuesto a que el JIT sabe más sobre la naturaleza de cadena de la matriz que tu [broma eliminada]. Anders etal hizo un gran trabajo optimizando todo sobre cadenas en .net

He hecho esto usando HashSet <char> y es aproximadamente 2 veces más lento que su método. Usar bool [] es apenas más rápido (0.0469ms / iter v. 0.0559ms / iter) que la versión que tiene en OP ... con el problema de ser menos legible.
user7116

1
No pude ver ninguna diferencia de rendimiento entre usar una matriz bool y una matriz int. Usaría una matriz bool, ya que reduce la tabla de búsqueda de 256 kb a 64 kb, pero sigue siendo una gran cantidad de datos para una función tan trivial ... Y es solo un 30% más rápido.
Guffa el

1
@Guffa 2) Dado que solo mantenemos caracteres alfanuméricos y algunos caracteres latinos básicos, solo necesitamos una tabla para el byte bajo, por lo que el tamaño no es realmente un problema. Si queríamos ser de propósito general, entonces la técnica estándar Unicode es la doble indirecta. En otras palabras, una tabla de 256 referencias de tabla, muchas de las cuales apuntan a la misma tabla vacía.
Steven Sudit

12
public static string RemoveSpecialCharacters(string str)
{
    char[] buffer = new char[str.Length];
    int idx = 0;

    foreach (char c in str)
    {
        if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z')
            || (c >= 'a' && c <= 'z') || (c == '.') || (c == '_'))
        {
            buffer[idx] = c;
            idx++;
        }
    }

    return new string(buffer, 0, idx);
}

1
+1, probado y es aproximadamente un 40% más rápido que StringBuilder. 0.0294ms / string v. 0.0399ms / string
user7116

Solo para estar seguro, ¿te refieres a StringBuilder con o sin asignación previa?
Steven Sudit

Con la preasignación, sigue siendo un 40% más lenta que la asignación char [] y la nueva cadena.
user7116

2
Me gusta esto. Ajusté este métodoforeach (char c in input.Where(c => char.IsLetterOrDigit(c) || allowedSpecialCharacters.Any(x => x == c))) buffer[idx++] = c;
Chris Marisic

11

Una expresión regular se verá así:

public string RemoveSpecialChars(string input)
{
    return Regex.Replace(input, @"[^0-9a-zA-Z\._]", string.Empty);
}

Pero si el rendimiento es muy importante, le recomiendo que haga algunos puntos de referencia antes de seleccionar la "ruta de expresión regular" ...


11

Si está utilizando una lista dinámica de caracteres, LINQ puede ofrecer una solución mucho más rápida y elegante:

public static string RemoveSpecialCharacters(string value, char[] specialCharacters)
{
    return new String(value.Except(specialCharacters).ToArray());
}

Comparé este enfoque con dos de los enfoques "rápidos" anteriores (compilación de lanzamiento):

  • Solución de matriz de char por LukeH - 427 ms
  • Solución StringBuilder - 429 ms
  • LINQ (esta respuesta) - 98 ms

Tenga en cuenta que el algoritmo se modifica ligeramente: los caracteres se pasan como una matriz en lugar de codificados, lo que podría tener un impacto leve en las cosas (es decir, / las otras soluciones tendrían un bucle interno para verificar la matriz de caracteres).

Si cambio a una solución codificada con una cláusula where de LINQ, los resultados son:

  • Solución de matriz de char - 7ms
  • Solución StringBuilder - 22ms
  • LINQ - 60 ms

Puede valer la pena mirar LINQ o un enfoque modificado si planea escribir una solución más genérica, en lugar de codificar la lista de caracteres. LINQ definitivamente le brinda un código conciso y altamente legible, incluso más que Regex.


3
Este enfoque se ve bien, pero no funciona: Excepto () es una operación establecida, por lo que terminará con solo la primera aparición de cada carácter único en la cadena.
McKenzieG1

5

No estoy convencido de que su algoritmo sea otra cosa que eficiente. Es O (n) y solo mira a cada personaje una vez. No obtendrá nada mejor que eso a menos que conozca mágicamente los valores antes de verificarlos.

Sin embargo, inicializaría la capacidad de tu StringBuilderal tamaño inicial de la cadena. Supongo que su problema de rendimiento percibido proviene de la reasignación de memoria.

Nota al margen: Comprobación A: zno es seguro. Estás incluyendo [, \, ], ^, _, y `...

Nota al margen 2: Para ese bit extra de eficiencia, coloque las comparaciones en un orden para minimizar el número de comparaciones. (En el peor de los casos, estás hablando de 8 comparaciones, así que no pienses demasiado). Esto cambia con tu aporte esperado, pero un ejemplo podría ser:

if (str[i] >= '0' && str[i] <= 'z' && 
    (str[i] >= 'a' || str[i] <= '9' ||  (str[i] >= 'A' && str[i] <= 'Z') || 
    str[i] == '_') || str[i] == '.')

Nota al margen 3: si por alguna razón REALMENTE necesita que esto sea rápido, una declaración de cambio puede ser más rápida. El compilador debe crear una tabla de salto para usted, lo que resulta en una sola comparación:

switch (str[i])
{
    case '0':
    case '1':
    .
    .
    .
    case '.':
        sb.Append(str[i]);
        break;
}

1
Estoy de acuerdo en que no puedes vencer a O (n) en este caso. Sin embargo, hay un costo por comparación que puede reducirse. Una búsqueda en la tabla tiene un costo bajo y fijo, mientras que una serie de comparaciones aumentará en costo a medida que agregue más excepciones.
Steven Sudit

Acerca de la nota al margen 3, ¿realmente crees que la mesa de salto sería más rápida que la búsqueda de mesa?
Steven Sudit

Ejecuté la prueba de rendimiento rápido en la solución del conmutador, y funciona igual que la comparación.
Guffa el

@Steven Sudit: me atrevería a decir que en realidad son casi lo mismo. ¿Quieres hacer una prueba?
lc.

77
La notación O (n) a veces me molesta. La gente hará suposiciones estúpidas basadas en el hecho de que el algoritmo ya es O (n). Si cambiamos esta rutina para reemplazar las llamadas str [i] con una función que recuperó el valor de comparación mediante la construcción de una conexión SSL única con un servidor en el lado opuesto del mundo ... está seguro de que vería un rendimiento masivo diferencia y el algoritmo es TODAVÍA O (n). ¡El costo de O (1) para cada algoritmo es significativo y NO equivalente!
darron el

4
StringBuilder sb = new StringBuilder();

for (int i = 0; i < fName.Length; i++)
{
   if (char.IsLetterOrDigit(fName[i]))
    {
       sb.Append(fName[i]);
    }
}

4

Puede usar la expresión regular de la siguiente manera:

return Regex.Replace(strIn, @"[^\w\.@-]", "", RegexOptions.None, TimeSpan.FromSeconds(1.0));

3

Me parece bien La única mejora que haría es inicializar StringBuildercon la longitud de la cadena.

StringBuilder sb = new StringBuilder(str.Length);

3

Estoy de acuerdo con este ejemplo de código. Lo único diferente es que lo convierto en Método de extensión de tipo de cadena. Para que pueda usarlo en una línea o código muy simple:

string test = "abc@#$123";
test.RemoveSpecialCharacters();

Gracias a Guffa por tu experimento.

public static class MethodExtensionHelper
    {
    public static string RemoveSpecialCharacters(this string str)
        {
            StringBuilder sb = new StringBuilder();
            foreach (char c in str)
            {
                if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
                {
                    sb.Append(c);
                }
            }
            return sb.ToString();
        }
}

2

Usaría un reemplazo de cadena con una expresión regular buscando "caracteres especiales", reemplazando todos los caracteres encontrados con una cadena vacía.


+1 ciertamente menos código y posiblemente más legible ignorando Regex de escritura única.
kenny el

1
@kenny: estoy de acuerdo. La pregunta original incluso afirma que las cadenas son cortas: 10-30 caracteres. Pero aparentemente mucha gente todavía piensa que estamos vendiendo tiempo de CPU por segundo ...
Tom Bushell

Reguler expressin funciona muy flojo, por lo que no debe usarse siempre.
RockOnGom

2

Tenía que hacer algo similar para el trabajo, pero en mi caso tuve que filtrar todo lo que no sea una letra, un número o un espacio en blanco (pero podría modificarlo fácilmente según sus necesidades). El filtrado se realiza del lado del cliente en JavaScript, pero por razones de seguridad también estoy haciendo el filtrado del lado del servidor. Como puedo esperar que la mayoría de las cadenas estén limpias, me gustaría evitar copiar la cadena a menos que realmente lo necesite. Esto me permite la implementación a continuación, que debería funcionar mejor tanto para cadenas limpias como sucias.

public static string EnsureOnlyLetterDigitOrWhiteSpace(string input)
{
    StringBuilder cleanedInput = null;
    for (var i = 0; i < input.Length; ++i)
    {
        var currentChar = input[i];
        var charIsValid = char.IsLetterOrDigit(currentChar) || char.IsWhiteSpace(currentChar);

        if (charIsValid)
        {
            if(cleanedInput != null)
                cleanedInput.Append(currentChar);
        }
        else
        {
            if (cleanedInput != null) continue;
            cleanedInput = new StringBuilder();
            if (i > 0)
                cleanedInput.Append(input.Substring(0, i));
        }
    }

    return cleanedInput == null ? input : cleanedInput.ToString();
}

1

Para S & G's, forma Linq-ified:

var original = "(*^%foo)(@)&^@#><>?:\":';=-+_";
var valid = new char[] { 
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 
    'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 
    'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 
    'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', 
    '9', '0', '.', '_' };
var result = string.Join("",
    (from x in original.ToCharArray() 
     where valid.Contains(x) select x.ToString())
        .ToArray());

Sin embargo, no creo que esta sea la forma más eficiente.


2
No lo es, porque es una búsqueda lineal.
Steven Sudit

1
public string RemoveSpecial(string evalstr)
{
StringBuilder finalstr = new StringBuilder();
            foreach(char c in evalstr){
            int charassci = Convert.ToInt16(c);
            if (!(charassci >= 33 && charassci <= 47))// special char ???
             finalstr.append(c);
            }
return finalstr.ToString();
}

1

Utilizar:

s.erase(std::remove_if(s.begin(), s.end(), my_predicate), s.end());

bool my_predicate(char c)
{
 return !(isalpha(c) || c=='_' || c==' '); // depending on you definition of special characters
}

Y obtendrás una cuerda limpia s.

erase()lo despojará de todos los caracteres especiales y es altamente personalizable con la my_predicate()función.


1

HashSet es O (1)
No estoy seguro si es más rápido que la comparación existente

private static HashSet<char> ValidChars = new HashSet<char>() { 'a', 'b', 'c', 'A', 'B', 'C', '1', '2', '3', '_' };
public static string RemoveSpecialCharacters(string str)
{
    StringBuilder sb = new StringBuilder(str.Length / 2);
    foreach (char c in str)
    {
        if (ValidChars.Contains(c)) sb.Append(c);
    }
    return sb.ToString();
}

Probé y esto no más rápido que la respuesta aceptada.
Lo dejaré como si necesitaras un conjunto de caracteres configurable, esta sería una buena solución.


¿Por qué crees que la comparación no es O (1)?
Guffa

@Guffa No estoy seguro de que no sea así y eliminé mi comentario. Y +1. Debería haber hecho más pruebas antes de hacer el comentario.
paparazzo

1

Me pregunto si un reemplazo basado en Regex (posiblemente compilado) es más rápido. Tendría que probar que alguien ha encontrado que esto es ~ 5 veces más lento.

Aparte de eso, debe inicializar el StringBuilder con una longitud esperada, para que la cadena intermedia no tenga que copiarse mientras crece.

Un buen número es la longitud de la cadena original, o algo ligeramente más bajo (dependiendo de la naturaleza de las entradas de funciones).

Finalmente, puede usar una tabla de búsqueda (en el rango 0..127) para averiguar si un personaje debe ser aceptado.


Ya se ha probado una expresión regular, y es aproximadamente cinco veces más lenta. Con una tabla de búsqueda en el rango 0..127, aún tiene que verificar el rango del código de caracteres antes de usar la tabla de búsqueda, ya que los caracteres son valores de 16 bits, no valores de 7 bits.
Guffa

@Guffa Err ... ¿sí? ;)
Christian Klauser

1

El siguiente código tiene el siguiente resultado (la conclusión es que también podemos guardar algunos recursos de memoria asignando un tamaño de matriz más pequeño):

lookup = new bool[123];

for (var c = '0'; c <= '9'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

for (var c = 'A'; c <= 'Z'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

for (var c = 'a'; c <= 'z'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

48: 0  
49: 1  
50: 2  
51: 3  
52: 4  
53: 5  
54: 6  
55: 7  
56: 8  
57: 9  
65: A  
66: B  
67: C  
68: D  
69: E  
70: F  
71: G  
72: H  
73: I  
74: J  
75: K  
76: L  
77: M  
78: N  
79: O  
80: P  
81: Q  
82: R  
83: S  
84: T  
85: U  
86: V  
87: W  
88: X  
89: Y  
90: Z  
97: a  
98: b  
99: c  
100: d  
101: e  
102: f  
103: g  
104: h  
105: i  
106: j  
107: k  
108: l  
109: m  
110: n  
111: o  
112: p  
113: q  
114: r  
115: s  
116: t  
117: u  
118: v  
119: w  
120: x  
121: y  
122: z  

También puede agregar las siguientes líneas de código para admitir la configuración regional rusa (el tamaño de la matriz será 1104):

for (var c = 'А'; c <= 'Я'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

for (var c = 'а'; c <= 'я'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

1

No estoy seguro de que sea la forma más eficiente, pero funciona para mí.

 Public Function RemoverTildes(stIn As String) As String
    Dim stFormD As String = stIn.Normalize(NormalizationForm.FormD)
    Dim sb As New StringBuilder()

    For ich As Integer = 0 To stFormD.Length - 1
        Dim uc As UnicodeCategory = CharUnicodeInfo.GetUnicodeCategory(stFormD(ich))
        If uc <> UnicodeCategory.NonSpacingMark Then
            sb.Append(stFormD(ich))
        End If
    Next
    Return (sb.ToString().Normalize(NormalizationForm.FormC))
End Function

La respuesta hace el trabajo, pero la pregunta era para C #. (PD: Sé que esto fue hace casi cinco años, pero aún así ...) Usé el convertidor VB a C # de Telerik, (y viceversa) y el código funcionó bien, aunque no estoy seguro de nadie más. (Otra cosa, converter.telerik.com )
Momoro

1

Aquí hay muchas soluciones propuestas, algunas más eficientes que otras, pero quizás no muy legibles. Aquí hay uno que puede no ser el más eficiente, pero ciertamente utilizable para la mayoría de las situaciones, y es bastante conciso y legible, aprovechando Linq:

string stringToclean = "This is a test.  Do not try this at home; you might get hurt. Don't believe it?";

var validPunctuation = new HashSet<char>(". -");

var cleanedVersion = new String(stringToclean.Where(x => (x >= 'A' && x <= 'Z') || (x >= 'a' && x <= 'z') || validPunctuation.Contains(x)).ToArray());

var cleanedLowercaseVersion = new String(stringToclean.ToLower().Where(x => (x >= 'a' && x <= 'z') || validPunctuation.Contains(x)).ToArray());

-1
public static string RemoveSpecialCharacters(string str){
    return str.replaceAll("[^A-Za-z0-9_\\\\.]", "");
}

1
Me temo replaceAllque no es la función de cadena de C # sino Java o JavaScript
Csaba Toth

-1
public static string RemoveAllSpecialCharacters(this string text) {
  if (string.IsNullOrEmpty(text))
    return text;

  string result = Regex.Replace(text, "[:!@#$%^&*()}{|\":?><\\[\\]\\;'/.,~]", " ");
  return result;
}

La respuesta es incorrecta. Si va a usar expresiones regulares, debe ser inclusivo, no exclusivo, porque ahora extraña algunos caracteres. En realidad, ya hay una respuesta con expresiones regulares. Y para estar lleno, la expresión regular es MÁS LENTA que la función de comparación directa de caracteres.
TPAKTOPA

-3

Si le preocupa la velocidad, use punteros para editar la cadena existente. Puede anclar la cadena y obtener un puntero, luego ejecutar un bucle for sobre cada carácter, sobrescribiendo cada carácter no válido con un carácter de reemplazo. Sería extremadamente eficiente y no requeriría asignar ninguna nueva memoria de cadena. También necesitaría compilar su módulo con la opción insegura y agregar el modificador "inseguro" al encabezado de su método para usar punteros.

static void Main(string[] args)
{
    string str = "string!$%with^&*invalid!!characters";
    Console.WriteLine( str ); //print original string
    FixMyString( str, ' ' );
    Console.WriteLine( str ); //print string again to verify that it has been modified
    Console.ReadLine(); //pause to leave command prompt open
}


public static unsafe void FixMyString( string str, char replacement_char )
{
    fixed (char* p_str = str)
    {
        char* c = p_str; //temp pointer, since p_str is read-only
        for (int i = 0; i < str.Length; i++, c++) //loop through each character in string, advancing the character pointer as well
            if (!IsValidChar(*c)) //check whether the current character is invalid
                (*c) = replacement_char; //overwrite character in existing string with replacement character
    }
}

public static bool IsValidChar( char c )
{
    return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '.' || c == '_');
    //return char.IsLetterOrDigit( c ) || c == '.' || c == '_'; //this may work as well
}

14
Noooooooooo! ¡Cambiar una cadena en .NET es BAAAAAAAAAAAAD! Todo en el marco se basa en la regla de que las cuerdas son inmutables, y si lo rompes, puedes obtener efectos secundarios muy sorprendentes ...
Guffa el
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.