Самый эффективный способ удалить специальные символы из строки - PullRequest
243 голосов
/ 13 июля 2009

Я хочу удалить все специальные символы из строки. Допустимые символы: A-Z (заглавные или строчные), цифры (0-9), подчеркивание (_) или знак точки (.).

У меня есть следующее, это работает, но я подозреваю (я знаю!), Что это не очень эффективно:

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

Какой самый эффективный способ сделать это? Как будет выглядеть регулярное выражение, и как оно будет сравниваться с обычными манипуляциями со строками?

Строки, которые будут очищены, будут довольно короткими, обычно длиной от 10 до 30 символов.

Ответы [ 23 ]

3 голосов
/ 27 марта 2010
StringBuilder sb = new StringBuilder();

for (int i = 0; i < fName.Length; i++)
{
   if (char.IsLetterOrDigit(fName[i]))
    {
       sb.Append(fName[i]);
    }
}
2 голосов
/ 13 июля 2009

Я бы использовал String Replace с Regular Expression для поиска «специальных символов», заменяя все найденные символы пустой строкой.

2 голосов
/ 29 августа 2013

Я должен был сделать что-то подобное для работы, но в моем случае мне пришлось отфильтровать все, что не является буквой, цифрой или пробелом (но вы могли легко изменить это для своих нужд). Фильтрация выполняется на стороне клиента в JavaScript, но по соображениям безопасности я также выполняю фильтрацию на стороне сервера. Поскольку я могу ожидать, что большинство строк будет чистым, я хотел бы избежать копирования строки, если в этом нет особой необходимости. Это позволит перейти к реализации ниже, которая должна работать лучше как для чистых, так и для грязных строк.

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 голос
/ 03 января 2018

Здесь предлагается множество предлагаемых решений, некоторые из которых более эффективны, чем другие, но, возможно, не очень читабельны. Вот тот, который может быть не самым эффективным, но, безусловно, пригодным для использования в большинстве ситуаций, и он достаточно лаконичен и удобен для чтения, используя 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 голос
/ 13 июля 2009

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

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

Хорошее число - это длина исходной строки или что-то немного меньше (в зависимости от характера входных данных функций).

Наконец, выможно использовать справочную таблицу (в диапазоне 0..127), чтобы узнать, должен ли символ быть принят.

1 голос
/ 25 августа 2015

Я не уверен, что это самый эффективный способ, но он работает для меня

 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
1 голос
/ 24 сентября 2013

HashSet is O (1)
Не уверен, что это быстрее, чем существующее сравнение

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

Я проверил, и это не быстрее, чем принятый ответ.
Я оставлю это так, как будто вам нужен настраиваемый набор символов, это было бы хорошим решением.

1 голос
/ 23 сентября 2012

Использование:

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
}

И вы получите чистую строку s.

erase() удалит из него все специальные символы и обладает широкими возможностями настройки с помощью функции my_predicate().

1 голос
/ 19 апреля 2012

Следующий код имеет следующий вывод (вывод состоит в том, что мы также можем сохранить некоторые ресурсы памяти, выделяя массив меньшего размера):

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  

Вы также можете добавить следующие строки кода для поддержки русской локали (размер массива будет 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 голос
/ 13 июля 2009

Для S & G, 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());

Однако я не думаю, что это будет самый эффективный способ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...