C #: удаление общих недопустимых символов из строки: улучшить этот алгоритм - PullRequest
14 голосов
/ 25 августа 2009

Рассмотрим требование убрать недопустимые символы из строки. Символы просто необходимо удалить и заменить на пустые или string.Empty.

char[] BAD_CHARS = new char[] { '!', '@', '#', '$', '%', '_' }; //simple example

foreach (char bad in BAD_CHARS)
{
    if (someString.Contains(bad))
      someString = someString.Replace(bad.ToString(), string.Empty);
}

Мне бы очень понравилось , чтобы сделать это:

if (BAD_CHARS.Any(bc => someString.Contains(bc)))
    someString.Replace(bc,string.Empty); // bc is out of scope

Вопрос: Есть ли у вас какие-либо предложения по рефакторингу этого алгоритма или каких-либо более простых, удобных для чтения, эффективных, обслуживаемых алгоритмов?

Ответы [ 9 ]

34 голосов
/ 25 августа 2009

Я не знаю о его читабельности, но регулярное выражение может сделать то, что вам нужно:

someString = Regex.Replace(someString, @"[!@#$%_]", "");
22 голосов
/ 25 августа 2009
char[] BAD_CHARS = new char[] { '!', '@', '#', '$', '%', '_' }; //simple example
someString = string.Concat(someString.Split(BAD_CHARS,StringSplitOptions.RemoveEmptyEntries));

должно сработать (извините за любые мелкие синтаксические ошибки, которые у меня на телефоне)

18 голосов
/ 25 августа 2009

Класс string является неизменным (хотя и ссылочным типом), поэтому все его статические методы предназначены для возврата переменной new string. Вызов someString.Replace без присвоения ему чего-либо не повлияет на вашу программу. - Похоже, вы исправили эту проблему.

Основная проблема с вашим предложенным алгоритмом заключается в том, что он многократно присваивает много новых string переменных, что может привести к значительному снижению производительности. LINQ не очень помогает здесь. (На мой взгляд, я не делаю код значительно короче и, конечно, не более читабельным.)

Попробуйте следующий метод расширения. Ключом является использование StringBuilder, что означает, что для выполнения во время выполнения назначается только один блок памяти.

private static readonly HashSet<char> badChars = 
    new HashSet<char> { '!', '@', '#', '$', '%', '_' };

public static string CleanString(this string str)
{
    var result = new StringBuilder(str.Length);
    for (int i = 0; i < str.Length; i++)
    {
        if (!badChars.Contains(str[i]))
            result.Append(str[i]);
    }
    return result.ToString();
}

Этот алгоритм также использует класс .NET 3.5 'HashSet', чтобы дать O(1) время поиска для обнаружения плохого символа. Это делает общий алгоритм O(n) вместо O(nm) вашего опубликованного (m - количество плохих символов); это также намного лучше с использованием памяти, как объяснено выше.

7 голосов
/ 26 августа 2009

Этот на быстрее HashSet<T>. Кроме того, если вам приходится часто выполнять это действие, рассмотрите основы этого вопроса, который я задал здесь .

private static readonly bool[] BadCharValues;

static StaticConstructor()
{
    BadCharValues = new bool[char.MaxValue+1];
    char[] badChars = { '!', '@', '#', '$', '%', '_' };
    foreach (char c in badChars)
        BadCharValues[c] = true;
}

public static string CleanString(string str)
{
    var result = new StringBuilder(str.Length);
    for (int i = 0; i < str.Length; i++)
    {
        if (!BadCharValues[str[i]])
            result.Append(str[i]);
    }
    return result.ToString();
}
4 голосов
/ 30 апреля 2011

Дополнительный совет: если вы не хотите помнить массив char, недопустимый для файлов, вы можете использовать Path.GetInvalidFileNameChars(). Если вы хотели это для путей, это Path.GetInvalidPathChars

private static string RemoveInvalidChars(string str)
            {
                return string.Concat(str.Split(Path.GetInvalidFileNameChars(), StringSplitOptions.RemoveEmptyEntries));
            }
4 голосов
/ 25 августа 2009

, если вы все еще хотите сделать это LINQy:

public static string CleanUp(this string orig)
{
    var badchars = new HashSet<char>() { '!', '@', '#', '$', '%', '_' };

    return new string(orig.Where(c => !badchars.Contains(c)).ToArray());
}
3 голосов
/ 25 августа 2009

Что следует учесть - если это, скажем, для паролей, вы хотите отсканировать и сохранить хороших символов и предположить, что все остальное плохо. Проще правильно отфильтровать или хорошие вещи, а затем попытаться угадать все плохие вещи.

для каждого персонажа Если Символ Хороший -> Сохраните его (скопируйте в буфер, что угодно.)

1007 * Джеф *

3 голосов
/ 25 августа 2009

Почему вы ДЕЙСТВИТЕЛЬНО НРАВИТСЯ, чтобы сделать это? Код абсолютно не проще, вы просто вводите метод расширения запроса в ваш код.

Кроме того, проверка Contains представляется избыточной как концептуально, так и с точки зрения производительности. В любом случае Contains должен пройти через всю строку, вы можете просто вызвать Replace(bad.ToString(), string.Empty) для каждого символа и забыть о том, присутствует ли он на самом деле.

Конечно, регулярное выражение всегда является опцией и может быть более производительным (если не менее понятным) в такой ситуации.

2 голосов
/ 26 августа 2009

Это довольно чисто. Ограничивает его действительными символами вместо удаления недопустимых. Вы должны разделить его на константы, вероятно:

string clean = new string(@"Sour!ce Str&*(@ing".Where(c => 
@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ,.".Contains(c)).ToArray()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...