Sostituisci più caratteri in una stringa C #


179

C'è un modo migliore per sostituire le stringhe?

Sono sorpreso che Sostituisci non includa un array di caratteri o un array di stringhe. Immagino di poter scrivere la mia estensione ma ero curioso di sapere se esiste un modo migliore per fare quanto segue? Si noti che l'ultimo Sostituisci è una stringa non un carattere.

myString.Replace(';', '\n').Replace(',', '\n').Replace('\r', '\n').Replace('\t', '\n').Replace(' ', '\n').Replace("\n\n", "\n");

Risposte:


207

È possibile utilizzare un'espressione regolare sostitutiva.

s/[;,\t\r ]|[\n]{2}/\n/g
  • s/ all'inizio significa una ricerca
  • I personaggi tra [e] sono i caratteri da cercare (in qualsiasi ordine)
  • Il secondo /delimita il testo di ricerca e il testo di sostituzione

In inglese, si legge:

"Cerca ;o ,o \to \ro (spazio) o esattamente due sequenziali \ne sostituiscilo con \n"

In C #, è possibile effettuare le seguenti operazioni: (dopo l'importazione System.Text.RegularExpressions)

Regex pattern = new Regex("[;,\t\r ]|[\n]{2}");
pattern.Replace(myString, "\n");

2
\te \rsono inclusi in \s. Quindi la tua regex è equivalente a [;,\s].
NullUserException

3
Ed \sè in realtà equivalente a [ \f\n\r\t\v]quindi stai includendo alcune cose lì che non erano nella domanda originale. Inoltre, la domanda originale richiede Replace("\n\n", "\n")quale regex non gestisce.
NullUserException

11
Per le semplici operazioni di sostituzione che non sono configurabili da un utente, l'utilizzo delle espressioni regolari non è ottimale poiché è molto lento rispetto alle normali operazioni di stringa, secondo un primo articolo di riferimento che ho trovato durante la ricerca di "sostituzione prestazioni c # regex" è circa 13 tempi più lenti.
troppo il

Ah regex, i geroglifici del potere! L'unico problema che posso vedere qui è la leggibilità umana delle espressioni regolari; molti si rifiutano di capirli. Di recente ho aggiunto una soluzione di seguito per coloro che cercano un'alternativa meno complessa.
sɐunıɔ ןɐ qɐp

Quindi, come possiamo scrivere se vogliamo sostituire più caratteri con più caratteri?
Habip Oğuz,

114

Se ti senti particolarmente intelligente e non vuoi usare Regex:

char[] separators = new char[]{' ',';',',','\r','\t','\n'};

string s = "this;is,\ra\t\n\n\ntest";
string[] temp = s.Split(separators, StringSplitOptions.RemoveEmptyEntries);
s = String.Join("\n", temp);

Puoi avvolgerlo in un metodo di estensione anche con un piccolo sforzo.

Modifica: Oppure aspetta solo 2 minuti e finirò comunque per scriverlo :)

public static class ExtensionMethods
{
   public static string Replace(this string s, char[] separators, string newVal)
   {
       string[] temp;

       temp = s.Split(separators, StringSplitOptions.RemoveEmptyEntries);
       return String.Join( newVal, temp );
   }
}

E voilà ...

char[] separators = new char[]{' ',';',',','\r','\t','\n'};
string s = "this;is,\ra\t\n\n\ntest";

s = s.Replace(separators, "\n");

Molto inefficiente dalla memoria, specialmente per stringhe più grandi.
MarcinJuraszek,

@MarcinJuraszek Lol ... È probabilmente la prima volta che ascolto qualcuno affermare che i metodi di stringa incorporati sono meno efficienti in termini di memoria rispetto alle espressioni regolari.
Paul Walls,

10
Hai ragione. Avrei dovuto misurarlo prima di pubblicarlo. Eseguo benchmark ed Regex.Replaceè oltre 8 volte più lento di più string.Replacechiamate di fila. e 4x più lento di Split+ Join. Vedi gist.github.com/MarcinJuraszek/c1437d925548561ba210a1c6ed144452
MarcinJuraszek

1
Bella soluzione! solo un piccolo addon. Sfortunatamente, questo non funzionerà se si desidera sostituire anche il primo personaggio. Supponi di voler sostituire il carattere 't' nella stringa di esempio. Il metodo Split eliminerà semplicemente quella "t" della prima parola "this" perché è un oggetto EmptyEntry. Se si utilizza StringSplitOptions.None invece di RemoveEmptyEntries, Split lascerà la voce e il metodo Join aggiungerà invece il carattere separatore. Spero che questo aiuti
Pierre,

58

È possibile utilizzare la funzione aggregata di Linq:

string s = "the\nquick\tbrown\rdog,jumped;over the lazy fox.";
char[] chars = new char[] { ' ', ';', ',', '\r', '\t', '\n' };
string snew = chars.Aggregate(s, (c1, c2) => c1.Replace(c2, '\n'));

Ecco il metodo di estensione:

public static string ReplaceAll(this string seed, char[] chars, char replacementCharacter)
{
    return chars.Aggregate(seed, (str, cItem) => str.Replace(cItem, replacementCharacter));
}

Esempio di utilizzo del metodo di estensione:

string snew = s.ReplaceAll(chars, '\n');

21

Questo è il modo più breve:

myString = Regex.Replace(myString, @"[;,\t\r ]|[\n]{2}", "\n");

1
Questa fodera aiuta anche quando ne hai bisogno negli inizializzatori.
Guney Ozsan,

8

Ohhh, l'orrore della performance! La risposta è un po 'datata, ma comunque ...

public static class StringUtils
{
    #region Private members

    [ThreadStatic]
    private static StringBuilder m_ReplaceSB;

    private static StringBuilder GetReplaceSB(int capacity)
    {
        var result = m_ReplaceSB;

        if (null == result)
        {
            result = new StringBuilder(capacity);
            m_ReplaceSB = result;
        }
        else
        {
            result.Clear();
            result.EnsureCapacity(capacity);
        }

        return result;
    }


    public static string ReplaceAny(this string s, char replaceWith, params char[] chars)
    {
        if (null == chars)
            return s;

        if (null == s)
            return null;

        StringBuilder sb = null;

        for (int i = 0, count = s.Length; i < count; i++)
        {
            var temp = s[i];
            var replace = false;

            for (int j = 0, cc = chars.Length; j < cc; j++)
                if (temp == chars[j])
                {
                    if (null == sb)
                    {
                        sb = GetReplaceSB(count);
                        if (i > 0)
                            sb.Append(s, 0, i);
                    }

                    replace = true;
                    break;
                }

            if (replace)
                sb.Append(replaceWith);
            else
                if (null != sb)
                    sb.Append(temp);
        }

        return null == sb ? s : sb.ToString();
    }
}

7

Le stringhe sono solo immutabili array di caratteri

Devi solo renderlo mutabile:

  • o usando StringBuilder
  • andare nel unsafemondo e giocare con i puntatori (pericoloso però)

e prova a scorrere la serie di caratteri il minor numero di volte. Nota HashSetqui, poiché evita di attraversare la sequenza di caratteri all'interno del loop. Se hai bisogno di una ricerca ancora più veloce, puoi sostituirla HashSetcon una ricerca ottimizzata per char(basata su un array[256]).

Esempio con StringBuilder

public static void MultiReplace(this StringBuilder builder, 
    char[] toReplace, 
    char replacement)
{
    HashSet<char> set = new HashSet<char>(toReplace);
    for (int i = 0; i < builder.Length; ++i)
    {
        var currentCharacter = builder[i];
        if (set.Contains(currentCharacter))
        {
            builder[i] = replacement;
        }
    }
}

Modifica: versione ottimizzata

public static void MultiReplace(this StringBuilder builder, 
    char[] toReplace,
    char replacement)
{
    var set = new bool[256];
    foreach (var charToReplace in toReplace)
    {
        set[charToReplace] = true;
    }
    for (int i = 0; i < builder.Length; ++i)
    {
        var currentCharacter = builder[i];
        if (set[currentCharacter])
        {
            builder[i] = replacement;
        }
    }
}

Quindi lo usi in questo modo:

var builder = new StringBuilder("my bad,url&slugs");
builder.MultiReplace(new []{' ', '&', ','}, '-');
var result = builder.ToString();

Ricorda che le stringhe sono wchar_tin .net, stai sostituendo solo un sottoinsieme di tutti i possibili caratteri (e avrai bisogno di 65536 bool per ottimizzarlo ...)
gog

3

Puoi anche semplicemente scrivere questi metodi di estensione delle stringhe e metterli da qualche parte nella tua soluzione:

using System.Text;

public static class StringExtensions
{
    public static string ReplaceAll(this string original, string toBeReplaced, string newValue)
    {
        if (string.IsNullOrEmpty(original) || string.IsNullOrEmpty(toBeReplaced)) return original;
        if (newValue == null) newValue = string.Empty;
        StringBuilder sb = new StringBuilder();
        foreach (char ch in original)
        {
            if (toBeReplaced.IndexOf(ch) < 0) sb.Append(ch);
            else sb.Append(newValue);
        }
        return sb.ToString();
    }

    public static string ReplaceAll(this string original, string[] toBeReplaced, string newValue)
    {
        if (string.IsNullOrEmpty(original) || toBeReplaced == null || toBeReplaced.Length <= 0) return original;
        if (newValue == null) newValue = string.Empty;
        foreach (string str in toBeReplaced)
            if (!string.IsNullOrEmpty(str))
                original = original.Replace(str, newValue);
        return original;
    }
}


Chiamali così:

"ABCDE".ReplaceAll("ACE", "xy");

xyBxyDxy


E questo:

"ABCDEF".ReplaceAll(new string[] { "AB", "DE", "EF" }, "xy");

xyCxyF


2

Usa RegEx.Replace, qualcosa del genere:

  string input = "This is   text with   far  too   much   " + 
                 "whitespace.";
  string pattern = "[;,]";
  string replacement = "\n";
  Regex rgx = new Regex(pattern);
  string result = rgx.Replace(input, replacement);

Ecco ulteriori informazioni su questa documentazione MSDN per RegEx.Replace


1

Per quanto riguarda le prestazioni, questa potrebbe non essere la soluzione migliore ma funziona.

var str = "filename:with&bad$separators.txt";
char[] charArray = new char[] { '#', '%', '&', '{', '}', '\\', '<', '>', '*', '?', '/', ' ', '$', '!', '\'', '"', ':', '@' };
foreach (var singleChar in charArray)
{
   str = str.Replace(singleChar, '_');
}

1
string ToBeReplaceCharacters = @"~()@#$%&amp;+,'&quot;&lt;&gt;|;\/*?";
string fileName = "filename;with<bad:separators?";

foreach (var RepChar in ToBeReplaceCharacters)
{
    fileName = fileName.Replace(RepChar.ToString(), "");
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.