Видаліть символи з рядка C #


150

Як я можу видалити символи з рядка? Наприклад: "My name @is ,Wan.;'; Wan".

Я хотів би видалити символи '@', ',', '.', ';', '\''з цього рядка, щоб він став"My name is Wan Wan"

Відповіді:


177
var str = "My name @is ,Wan.;'; Wan";
var charsToRemove = new string[] { "@", ",", ".", ";", "'" };
foreach (var c in charsToRemove)
{
    str = str.Replace(c, string.Empty);
}

Але я можу запропонувати інший підхід, якщо ви хочете видалити всі символи, які не букви

var str = "My name @is ,Wan.;'; Wan";
str = new string((from c in str
                  where char.IsWhiteSpace(c) || char.IsLetterOrDigit(c)
                  select c
       ).ToArray());

12
Також можна зробити так, str = new string (str.Where (x => char.IsWhiteSpace (x) || char.IsLetterOrDigit (x)). ToArray ());
Аднан Бхатті

1
Мені довелося шукати це, string.Empty не створює рядок для порівняння, тому це більш ефективно, ніж "". ( Stackoverflow.com/questions/151472 / ... )
Том Cerul

6
Я єдиний, хто отримує "Аргумент 2: не можу перетворити з" string "в" char "" om string.Empty?
OddDev

2
@OddDev ви повинні отримати цю помилку, лише якщо ваш масив, через який ви перебуваєте, є списком символів. Якщо вони є рядками, це має спрацювати
Newteq Developer

3
Крім того, зауважте, що для функції "str.Replace" для нормальної роботи, перший параметр повинен бути "рядком", якщо ви хочете використовувати рядок. Порожній як другий параметр. Якщо ви використовуєте char (тобто "a") в якості першого параметра, вам також знадобиться знак як другий. В іншому випадку ви отримаєте помилку "Аргумент 2: не вдається перетворити з" string "в" char "", згадану @OddDev вище
Лев


64

Здається, ідеальний додаток для RegEx - двигуна, призначеного для швидкого маніпулювання текстом. В цьому випадку:

Regex.Replace("He\"ll,o Wo'r.ld", "[@,\\.\";'\\\\]", string.Empty)

3
Схоже, це було б набагато ефективніше, ніж підхід на основі ітератора, особливо якщо ви можете використовувати компільований Regex;
Аде Міллер

Це має бути прийнятою відповіддю, тим більше, що, як сказав @AdeMiller, це буде набагато ефективнішим.
Обсидіан

14
Це не швидше, ніж цикл, це поширена помилка, що регулярні вирази завжди швидші, ніж петлі. Regex не є магією, за своєю суттю вони повинні в якийсь момент перейти через рядок для виконання своїх операцій, і вони можуть бути набагато повільнішими за накладні витрати від самого регулярного виразу. Вони справді видатні, якщо мова йде про надзвичайно складну маніпуляцію, де знадобляться десятки рядків коду та декілька циклів. Випробовуючи скомпільовану версію цього регулярного виразів на простому неоптимізованому циклі 50000 разів, регулярний вираз стає на 6 разів повільніше.
Тоні Чітем

А як щодо ефективності пам'яті? Чи не регулярні вирази будуть ефективнішими у сенсі розподілу нових рядків?
Марек

2
Можливо, я пропустив помилку, коли запевнив, що RegEx швидкий. Якщо це не було в центрі дуже жорсткого циклу, то інші міркування, така читабельність та ремонтопридатність, ймовірно, домінуватимуть над ефективністю для такої невеликої операції.
Джон Мелвілл

21

Менш специфічні для вашого запитання, можна видалити ВСІ розділові знаки з рядка (крім пробілу) білим переліком прийнятних символів у звичайному виразі:

string dirty = "My name @is ,Wan.;'; Wan";

// only space, capital A-Z, lowercase a-z, and digits 0-9 are allowed in the string
string clean = Regex.Replace(dirty, "[^A-Za-z0-9 ]", "");

Зауважте, що після цього є пробіл, щоб не видаляти пробіли у реченні. Третій аргумент - порожній рядок, який служить для заміни будь-якої підрядки, яка не належить до регулярного виразу.


19

Порівняння різних пропозицій (а також порівняння в контексті заміни однозначних символів з різними розмірами та положеннями цілі).

У цьому конкретному випадку розбиття на цілі та приєднання до заміни (у цьому випадку порожній рядок) є найшвидшим щонайменше на коефіцієнт 3. Зрештою, продуктивність відрізняється залежно від кількості замін, де заміни знаходяться в джерело та розмір джерела. #ymmv

Результати

(повні результати тут )

| Test                      | Compare | Elapsed                                                            |
|---------------------------|---------|--------------------------------------------------------------------|
| SplitJoin                 | 1.00x   | 29023 ticks elapsed (2.9023 ms) [in 10K reps, 0.00029023 ms per]   |
| Replace                   | 2.77x   | 80295 ticks elapsed (8.0295 ms) [in 10K reps, 0.00080295 ms per]   |
| RegexCompiled             | 5.27x   | 152869 ticks elapsed (15.2869 ms) [in 10K reps, 0.00152869 ms per] |
| LinqSplit                 | 5.43x   | 157580 ticks elapsed (15.758 ms) [in 10K reps, 0.0015758 ms per]   |
| Regex, Uncompiled         | 5.85x   | 169667 ticks elapsed (16.9667 ms) [in 10K reps, 0.00169667 ms per] |
| Regex                     | 6.81x   | 197551 ticks elapsed (19.7551 ms) [in 10K reps, 0.00197551 ms per] |
| RegexCompiled Insensitive | 7.33x   | 212789 ticks elapsed (21.2789 ms) [in 10K reps, 0.00212789 ms per] |
| Regex Insentive           | 7.52x   | 218164 ticks elapsed (21.8164 ms) [in 10K reps, 0.00218164 ms per] |

Тестовий джгут (LinqPad)

(Примітка: Perfі Vsє розширеннями синхронізації я написав )

void test(string title, string sample, string target, string replacement) {
    var targets = target.ToCharArray();

    var tox = "[" + target + "]";
    var x = new Regex(tox);
    var xc = new Regex(tox, RegexOptions.Compiled);
    var xci = new Regex(tox, RegexOptions.Compiled | RegexOptions.IgnoreCase);

    // no, don't dump the results
    var p = new Perf/*<string>*/();
        p.Add(string.Join(" ", title, "Replace"), n => targets.Aggregate(sample, (res, curr) => res.Replace(new string(curr, 1), replacement)));
        p.Add(string.Join(" ", title, "SplitJoin"), n => String.Join(replacement, sample.Split(targets)));
        p.Add(string.Join(" ", title, "LinqSplit"), n => String.Concat(sample.Select(c => targets.Contains(c) ? replacement : new string(c, 1))));
        p.Add(string.Join(" ", title, "Regex"), n => Regex.Replace(sample, tox, replacement));
        p.Add(string.Join(" ", title, "Regex Insentive"), n => Regex.Replace(sample, tox, replacement, RegexOptions.IgnoreCase));
        p.Add(string.Join(" ", title, "Regex, Uncompiled"), n => x.Replace(sample, replacement));
        p.Add(string.Join(" ", title, "RegexCompiled"), n => xc.Replace(sample, replacement));
        p.Add(string.Join(" ", title, "RegexCompiled Insensitive"), n => xci.Replace(sample, replacement));

    var trunc = 40;
    var header = sample.Length > trunc ? sample.Substring(0, trunc) + "..." : sample;

    p.Vs(header);
}

void Main()
{
    // also see /programming/7411438/remove-characters-from-c-sharp-string

    "Control".Perf(n => { var s = "*"; });


    var text = "My name @is ,Wan.;'; Wan";
    var clean = new[] { '@', ',', '.', ';', '\'' };

    test("stackoverflow", text, string.Concat(clean), string.Empty);


    var target = "o";
    var f = "x";
    var replacement = "1";

    var fillers = new Dictionary<string, string> {
        { "short", new String(f[0], 10) },
        { "med", new String(f[0], 300) },
        { "long", new String(f[0], 1000) },
        { "huge", new String(f[0], 10000) }
    };

    var formats = new Dictionary<string, string> {
        { "start", "{0}{1}{1}" },
        { "middle", "{1}{0}{1}" },
        { "end", "{1}{1}{0}" }
    };

    foreach(var filler in fillers)
    foreach(var format in formats) {
        var title = string.Join("-", filler.Key, format.Key);
        var sample = string.Format(format.Value, target, filler.Value);

        test(title, sample, target, replacement);
    }
}

1
Нарешті кілька номерів! Гарна робота @drzaus!
Марек



6

Ще одне просте рішення:

var forbiddenChars = @"@,.;'".ToCharArray();
var dirty = "My name @is ,Wan.;'; Wan";
var clean = new string(dirty.Where(c => !forbiddenChars.Contains(c)).ToArray());


4

Рядок - це просто масив символів, тому використовуйте Linq для заміни (подібно до Albin вище, за винятком випадків, коли для заміни використовується оператор linq містить):

var resultString = new string(
        (from ch in "My name @is ,Wan.;'; Wan"
         where ! @"@,.;\'".Contains(ch)
         select ch).ToArray());

Перший рядок - це рядок для заміни символів, а другий - простий рядок, що містить символи


Рішення Альбіна Linq, напевно, краще, якщо тільки немає додаткових символів, які ви хочете відфільтрувати (не охоплені пробілом, літерами та цифрами).
alistair

3

Я б також міг викинути це тут.

Зробіть розширення, щоб видалити символи з рядка:

public static string RemoveChars(this string input, params char[] chars)
{
    var sb = new StringBuilder();
    for (int i = 0; i < input.Length; i++)
    {
        if (!chars.Contains(input[i]))
            sb.Append(input[i]);
    }
    return sb.ToString();
}

І це можна використовувати так:

string str = "My name @is ,Wan.;'; Wan";
string cleanedUpString = str.RemoveChars('@', ',', '.', ';', '\'');

Або просто так:

string str = "My name @is ,Wan.;'; Wan".RemoveChars('@', ',', '.', ';', '\'');

Це найкраще рішення, оскільки воно робить найменшу кількість пам'яті. Я також встановив би довжину вихідної рядки як початкову ємність конструктора рядків, як-от: new StringBuilder (input.Length) для цього, щоб мати найменшу кількість пам'яті.
treaschf

3

Здається, що найкоротшим способом є поєднання LINQ та string.Concat:

var input = @"My name @is ,Wan.;'; Wan";
var chrs = new[] {'@', ',', '.', ';', '\''};
var result = string.Concat(input.Where(c => !chrs.Contains(c)));
// => result = "My name is Wan Wan" 

Дивіться демонстрацію C # . Зауважте, що string.Concatце ярлик до string.Join("", ...).

Зауважте, що використовувати регулярний вираз для видалення окремих відомих символів все ж можна будувати динамічно, хоча вважається, що регулярний вираз є повільнішим. Однак ось спосіб побудувати такий динамічний регулярний вираз (де все, що вам потрібно, це клас символів):

var pattern = $"[{Regex.Escape(new string(chrs))}]+";
var result = Regex.Replace(input, pattern, string.Empty);

Дивіться ще одну демонстрацію C # . Регулярний вираз буде виглядати [@,\.;']+( в відповідності з однієї або більше ( +) послідовними появами @, ,, ., ;або 'символи) , де точка не повинна бути екранована, але Regex.Escapeбуде необхідно , щоб уникнути інших символів , які повинні бути екрановані, як \, ^, ]або -положення яких всередині класу символів ви не можете передбачити.



3

Ось метод, про який я писав, який має дещо інший підхід. Замість того, щоб вказувати символи, які потрібно видалити, я розказую свій метод, які символи я хочу зберегти - він видалить усі інші символи.

У прикладі ОП він хоче лише зберегти алфавітні символи та пробіли. Ось як виглядатиме дзвінок до мого методу ( C # demo ):

var str = "My name @is ,Wan.;'; Wan";

// "My name is Wan Wan"
var result = RemoveExcept(str, alphas: true, spaces: true);

Ось мій метод:

/// <summary>
/// Returns a copy of the original string containing only the set of whitelisted characters.
/// </summary>
/// <param name="value">The string that will be copied and scrubbed.</param>
/// <param name="alphas">If true, all alphabetical characters (a-zA-Z) will be preserved; otherwise, they will be removed.</param>
/// <param name="numerics">If true, all alphabetical characters (a-zA-Z) will be preserved; otherwise, they will be removed.</param>
/// <param name="dashes">If true, all alphabetical characters (a-zA-Z) will be preserved; otherwise, they will be removed.</param>
/// <param name="underlines">If true, all alphabetical characters (a-zA-Z) will be preserved; otherwise, they will be removed.</param>
/// <param name="spaces">If true, all alphabetical characters (a-zA-Z) will be preserved; otherwise, they will be removed.</param>
/// <param name="periods">If true, all decimal characters (".") will be preserved; otherwise, they will be removed.</param>
public static string RemoveExcept(string value, bool alphas = false, bool numerics = false, bool dashes = false, bool underlines = false, bool spaces = false, bool periods = false) {
    if (string.IsNullOrWhiteSpace(value)) return value;
    if (new[] { alphas, numerics, dashes, underlines, spaces, periods }.All(x => x == false)) return value;

    var whitelistChars = new HashSet<char>(string.Concat(
        alphas ? "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" : "",
        numerics ? "0123456789" : "",
        dashes ? "-" : "",
        underlines ? "_" : "",
        periods ? "." : "",
        spaces ? " " : ""
    ).ToCharArray());

    var scrubbedValue = value.Aggregate(new StringBuilder(), (sb, @char) => {
        if (whitelistChars.Contains(@char)) sb.Append(@char);
        return sb;
    }).ToString();

    return scrubbedValue;
}

Дивовижна відповідь!
edtheprogrammerguy

Дуже хороша! рядок чисел має 0 в два рази.
Джон Курц

@JohnKurtz Хороший улов - зараз його немає.
Mass Dot Net

2

Тут є багато хороших відповідей, ось моє доповнення разом із кількома одиничними тестами, які можуть бути використані, щоб допомогти перевірити правильність, моє рішення подібне до вищевказаного @ Rianne, але використовує ISet, щоб забезпечити час пошуку O (1) на символах заміни (а також схожий на рішення Linq @Albin Sunnanbo).

    using System;
    using System.Collections.Generic;
    using System.Linq;

    /// <summary>
    /// Returns a string with the specified characters removed.
    /// </summary>
    /// <param name="source">The string to filter.</param>
    /// <param name="removeCharacters">The characters to remove.</param>
    /// <returns>A new <see cref="System.String"/> with the specified characters removed.</returns>
    public static string Remove(this string source, IEnumerable<char> removeCharacters)
    {
        if (source == null)
        {
            throw new  ArgumentNullException("source");
        }

        if (removeCharacters == null)
        {
            throw new ArgumentNullException("removeCharacters");
        }

        // First see if we were given a collection that supports ISet
        ISet<char> replaceChars = removeCharacters as ISet<char>;

        if (replaceChars == null)
        {
            replaceChars = new HashSet<char>(removeCharacters);
        }

        IEnumerable<char> filtered = source.Where(currentChar => !replaceChars.Contains(currentChar));

        return new string(filtered.ToArray());
    }

Тут проходять тести NUnit (2.6+)

using System;
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;

[TestFixture]
public class StringExtensionMethodsTests
{
    [TestCaseSource(typeof(StringExtensionMethodsTests_Remove_Tests))]
    public void Remove(string targetString, IEnumerable<char> removeCharacters, string expected)
    {
        string actual = StringExtensionMethods.Remove(targetString, removeCharacters);

        Assert.That(actual, Is.EqualTo(expected));
    }

    [TestCaseSource(typeof(StringExtensionMethodsTests_Remove_ParameterValidation_Tests))]
    public void Remove_ParameterValidation(string targetString, IEnumerable<char> removeCharacters)
    {
        Assert.Throws<ArgumentNullException>(() => StringExtensionMethods.Remove(targetString, removeCharacters));
    }
}

internal class StringExtensionMethodsTests_Remove_Tests : IEnumerable
{
    public IEnumerator GetEnumerator()
    {
        yield return new TestCaseData("My name @is ,Wan.;'; Wan", new char[] { '@', ',', '.', ';', '\'' }, "My name is Wan Wan").SetName("StringUsingCharArray");
        yield return new TestCaseData("My name @is ,Wan.;'; Wan", new HashSet<char> { '@', ',', '.', ';', '\'' }, "My name is Wan Wan").SetName("StringUsingISetCollection");
        yield return new TestCaseData(string.Empty, new char[1], string.Empty).SetName("EmptyStringNoReplacementCharactersYieldsEmptyString");
        yield return new TestCaseData(string.Empty, new char[] { 'A', 'B', 'C' }, string.Empty).SetName("EmptyStringReplacementCharsYieldsEmptyString");
        yield return new TestCaseData("No replacement characters", new char[1], "No replacement characters").SetName("StringNoReplacementCharactersYieldsString");
        yield return new TestCaseData("No characters will be replaced", new char[] { 'Z' }, "No characters will be replaced").SetName("StringNonExistantReplacementCharactersYieldsString");
        yield return new TestCaseData("AaBbCc", new char[] { 'a', 'C' }, "ABbc").SetName("CaseSensitivityReplacements");
        yield return new TestCaseData("ABC", new char[] { 'A', 'B', 'C' }, string.Empty).SetName("AllCharactersRemoved");
        yield return new TestCaseData("AABBBBBBCC", new char[] { 'A', 'B', 'C' }, string.Empty).SetName("AllCharactersRemovedMultiple");
        yield return new TestCaseData("Test That They Didn't Attempt To Use .Except() which returns distinct characters", new char[] { '(', ')' }, "Test That They Didn't Attempt To Use .Except which returns distinct characters").SetName("ValidateTheStringIsNotJustDistinctCharacters");
    }
}

internal class StringExtensionMethodsTests_Remove_ParameterValidation_Tests : IEnumerable
{
    public IEnumerator GetEnumerator()
    {
        yield return new TestCaseData(null, null);
        yield return new TestCaseData("valid string", null);
        yield return new TestCaseData(null, new char[1]);
    }
}

2

Його потужний метод я зазвичай використовую в тому ж випадку:

private string Normalize(string text)
{
        return string.Join("",
            from ch in text
            where char.IsLetterOrDigit(ch) || char.IsWhiteSpace(ch)
            select ch);
}

Насолоджуйтесь ...


1

Стара школа на місці копіювання / тупання:

  private static string RemoveDirtyCharsFromString(string in_string)
     {
        int index = 0;
        int removed = 0;

        byte[] in_array = Encoding.UTF8.GetBytes(in_string);

        foreach (byte element in in_array)
        {
           if ((element == ' ') ||
               (element == '-') ||
               (element == ':'))
           {
              removed++;
           }
           else
           {
              in_array[index] = element;
              index++;
           }
        }

        Array.Resize<byte>(ref in_array, (in_array.Length - removed));
        return(System.Text.Encoding.UTF8.GetString(in_array, 0, in_array.Length));
     }

Не впевнені в ефективності інших методів (тобто, накладні витрати всіх викликів функцій та інстанцій, які є побічним ефектом при виконанні C #).


1

Я роблю це метод розширення та з рядковим масивом, я вважаю, що string[]це корисніше, ніж char[]тому, що char також може бути string:

public static class Helper
{
    public static string RemoverStrs(this string str, string[] removeStrs)
    {
        foreach (var removeStr in removeStrs)
            str = str.Replace(removeStr, "");
        return str;
    }
}

то ви можете використовувати його будь-де:

string myname = "My name @is ,Wan.;'; Wan";
string result = myname.RemoveStrs(new[]{ "@", ",", ".", ";", "\\"});

1

Мені потрібно було видалити спеціальні символи з XML-файлу. Ось як я це зробив. char.ToString () - герой цього коду.

string item = "<item type="line" />"
char DC4 = (char)0x14;
string fixed = item.Replace(DC4.ToString(), string.Empty);

1
new[] { ',', '.', ';', '\'', '@' }
.Aggregate("My name @is ,Wan.;'; Wan", (s, c) => s.Replace(c.ToString(), string.Empty)); 

1

Беручи показники продуктивності від @drzaus, ось метод розширення, який використовує найшвидший алгоритм.

public static class StringEx
{
    public static string RemoveCharacters(this string s, params char[] unwantedCharacters) 
        => s == null ? null : string.Join(string.Empty, s.Split(unwantedCharacters));
}

Використання

var name = "edward woodward!";
var removeDs = name.RemoveCharacters('d', '!');
Assert.Equal("ewar woowar", removeDs); // old joke
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.