Замініть кілька символів у рядку C #


178

Чи є кращий спосіб заміни рядків?

Я здивований, що Replace не бере масив символів або рядковий масив. Я думаю, що я міг би написати власне розширення, але мені було цікаво, чи є краще побудований спосіб зробити наступне? Зауважте, остання заміна - це рядок, не символ.

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

Відповіді:


206

Можна використовувати регулярний вираз заміни.

s/[;,\t\r ]|[\n]{2}/\n/g
  • s/ на початку означає пошук
  • Символи між [та] символи, які потрібно шукати (у будь-якому порядку)
  • Друга /розмежовує текст пошуку та заміни тексту

Англійською мовою це:

«Пошук ;або ,або \tабо \rабо (пропуск) або рівно два послідовних \nі замінити його \n»

У C # ви можете зробити наступне: (після імпорту System.Text.RegularExpressions)

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

2
\tі \rвключаються до \s. Отже, ваш регулярний вираз рівносильний [;,\s].
NullUserException

3
І \sнасправді еквівалентно [ \f\n\r\t\v]тому ви включаєте туди деякі речі, яких не було в первісному питанні. Крім того, в оригінальному запитанні задається питання, з Replace("\n\n", "\n")яким ваш регекс не справляється.
NullUserException

11
Зверніть увагу, що для простих операцій заміни, які не налаштовуються користувачем, використання регулярних виразів не є оптимальним, оскільки це дуже повільно порівняно з операціями звичайних рядків, згідно з першою базовою статтею, яку я знайшов під час пошуку "замінити продуктивність c # regex", це приблизно 13 рази повільніше.
теж

Ах гегекс, ієрогліфи влади! Єдине, що я можу тут побачити, - це читабельність регулярних виразів; багато хто відмовляється їх зрозуміти. Нещодавно я додав рішення нижче для тих, хто шукає менш складну альтернативу.
sɐunıɔ ןɐ qɐp

Тож як ми пишемо, якщо хочемо замінити кілька символів на кілька символів?
Хабіп Оуз

114

Якщо ви відчуваєте себе особливо розумними і не хочете користуватися 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);

Ви можете також обробити це методом розширення з невеликими зусиллями.

Редагувати: Або просто зачекайте 2 хвилини, і я все-таки закінчу його писати :)

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 );
   }
}

І вуаля ...

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

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

Дуже малоефективна пам'ять, особливо для великих струн.
MarcinJuraszek

@MarcinJuraszek Lol ... Це, мабуть, перший раз, коли я коли-небудь чув, що хтось стверджує, що вбудовані рядкові методи менш ефективні в пам'яті, ніж регулярні вирази.
Пол

10
Ти маєш рацію. Я повинен був виміряти, перш ніж я розмістив це. Я запускаю показник і Regex.Replaceна 8 разів повільніше, ніж кілька string.Replaceдзвінків поспіль. і в 4 рази повільніше Split+ Join. Дивіться gist.github.com/MarcinJuraszek/c1437d925548561ba210a1c6ed144452
MarcinJuraszek

1
Приємне рішення! лише невеликий аддон. На жаль, це не спрацює, якщо ви хочете, щоб також були замінені перші символи. Скажіть, що ви хочете замінити символ 't' у прикладі рядка. Метод Спліт просто скине це "t" першого слова "this", тому що це EmptyEntry. Якщо ви використовуєте StringSplitOptions.None замість RemoveEmptyEntries, Split залишить запис, а метод Join замість нього додасть символ роздільника. Сподіваюся, що це допомагає
П'єр

58

Ви можете використовувати функцію агрегації 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'));

Ось метод розширення:

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

Приклад використання методу розширення:

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

21

Це найкоротший шлях:

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

1
Цей один вкладиш також допомагає, коли вам це потрібно в ініціалізаторах.
Guney Ozsan

8

Ох, жах вистави! Відповідь трохи застаріла, але все ж ...

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

Струни - це просто незмінні масиви char

Вам просто потрібно зробити його змінним:

  • або за допомогою StringBuilder
  • ходити по unsafeсвіту і грати з вказівниками (хоча небезпечно)

і спробуйте повторити через масив символів найменшу кількість разів. Зверніть увагу наHashSet тут, оскільки це дозволяє уникнути проходження послідовності символів всередині циклу. Якщо вам потрібен ще швидший пошук, ви можете замінити HashSetйого оптимізованим пошуком char(на основі an array[256]).

Приклад із 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;
        }
    }
}

Редагувати - Оптимізована версія

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;
        }
    }
}

Тоді ви просто використовуєте це так:

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

Пам'ятайте , що рядки wchar_tв .net, ви замінюєте лише підмножина всіх можливих символів (і ви будете потребувати в 65536 Bools оптимізувати що ...)
GOG

3

Ви також можете просто написати ці способи розширення рядків і помістити їх десь у своє рішення:

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;
    }
}


Називайте їх так:

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

xyBxyDxy


І це:

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

xyCxyF



1

Performance-Wise це, мабуть, не найкраще рішення, але воно працює.

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(), "");
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.