Найефективніший спосіб видалення спеціальних символів з рядка


266

Я хочу видалити всі спеціальні символи з рядка. Дозволені символи - AZ (великі або малі), цифри (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 символів.


5
Я не ставлю це у відповідь, оскільки це не буде більш ефективним, але є ряд методів статичних знаків, таких як char.IsLetterOrDigit (), які ви можете використати у своєму операторі if, щоб зробити його хоча б більш розбірливим.
Мартін Харріс

5
Я не впевнений, що перевірка на A до z є безпечною, оскільки вона містить 6 символів, які не є алфавітними, лише один з яких бажаний (underbar).
Стівен Судіт

4
Зосередьтеся на тому, щоб зробити ваш код більш читабельним. якщо ви робите це в циклі, як 500 разів в секунду, ефективність не велика справа. Скористайтеся регулярним виразом, і це буде набагато простіше читати.l
Байрон

4
Байроне, ти, мабуть, маєш рацію щодо необхідності підкреслити читабельність. Однак я скептично ставлюсь до читабельності регекс. :-)
Стівен Судіт

2
Регулярні вирази, що читаються чи ні, є подібними до читання німецької мови чи ні; це залежить від того, знаєте ви це чи ні (хоча в обох випадках ви будете раз у раз натрапляти на граматичні правила, які не мають сенсу;)
Blixt

Відповіді:


325

Чому ви вважаєте, що ваш метод не ефективний? Це насправді один з найефективніших способів, яким ви можете це зробити.

Звичайно, слід прочитати символ у локальній змінній або використовувати перечислювач для зменшення кількості доступу до масиву:

public static string RemoveSpecialCharacters(this string str) {
   StringBuilder sb = new StringBuilder();
   foreach (char c in str) {
      if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '.' || c == '_') {
         sb.Append(c);
      }
   }
   return sb.ToString();
}

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

Редагувати:
Я зробив швидкий тест на працездатність, виконавши кожну функцію мільйон разів, рядок з 24 символами. Ось такі результати:

Оригінальна функція: 54,5 мс.
Моя запропонована зміна: 47,1 мс.
Шахта з налаштуванням StringBuilder ємністю: 43,3 мс.
Регулярний вираз: 294,4 мс.

Редагувати 2: Я додав відмінність між AZ та az у наведеному вище коді. (Я перевірив працездатність, і помітної різниці немає.)

Редагування 3:
Я протестував рішення lookup + char [], і воно працює приблизно за 13 мс.

Ціна, яку потрібно заплатити, - це, звичайно, ініціалізація величезної таблиці пошуку та збереження її в пам’яті. Ну, це не так багато даних, але це багато для такої тривіальної функції ...

private static bool[] _lookup;

static Program() {
   _lookup = new bool[65536];
   for (char c = '0'; c <= '9'; c++) _lookup[c] = true;
   for (char c = 'A'; c <= 'Z'; c++) _lookup[c] = true;
   for (char c = 'a'; c <= 'z'; c++) _lookup[c] = true;
   _lookup['.'] = true;
   _lookup['_'] = true;
}

public static string RemoveSpecialCharacters(string str) {
   char[] buffer = new char[str.Length];
   int index = 0;
   foreach (char c in str) {
      if (_lookup[c]) {
         buffer[index] = c;
         index++;
      }
   }
   return new string(buffer, 0, index);
}

4
Я згоден. Єдине інше, що я вніс би - це додати початковий аргумент ємності до конструктора StringBuilder, "= новий StringBuilder (str.Length)".
Девід

2
Моя відповідь, використовуючи char[]буфер, а не StringBuilder, має невелику перевагу в цьому відповідно до моїх тестувань. (У мене менше читається, тому невелика користь від продуктивності, мабуть, не варта.)
LukeH

1
@Steven: Це може бути так, але орієнтири говорять самі за себе! У моїх тестах використання char[]буфера працює (трохи) краще, ніж StringBuilderнавіть при масштабуванні до рядків довжиною десятки тисяч символів.
ЛукаХ

10
@downvoter: Чому downvote? Якщо ви не поясните, що ви вважаєте неправильним, це не може покращити відповідь.
Guffa

2
@SILENT: Ні, це не так, але ви повинні зробити це лише один раз. Якщо ви виділяєте масив, який є великим щоразу, коли ви викликаєте метод (і якщо ви часто називаєте метод), метод стає найповільнішим на сьогоднішній день і спричиняє багато роботи для збору сміття.
Гуффа

195

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

Для додаткової продуктивності ви можете або попередньо скласти його, або просто сказати його складати під час першого дзвінка (наступні дзвінки будуть швидшими.)

public static string RemoveSpecialCharacters(string str)
{
    return Regex.Replace(str, "[^a-zA-Z0-9_.]+", "", RegexOptions.Compiled);
}

1
Я здогадуюсь, що це, мабуть, досить складний запит, що це було б швидше, ніж підхід ОП, особливо якщо попередньо складено. Однак я не маю доказів, що підтверджують це. Це слід перевірити. Якщо це не різко повільніше, я б обрав такий підхід незалежно, оскільки його легше читати та підтримувати. +1
rmeador

6
Це дуже простий регекс (ніяких зворотних трендів чи будь-яких складних речей там), тому він повинен бути досить чортово швидким.

9
@rmeador: без його складання він приблизно в 5 разів повільніше, компільований - у 3 рази повільніше, ніж його метод. І все-таки 10 разів простіше, хоча :-D
user7116

6
Регулярні вирази не є магічними молотками і ніколи не швидшими, ніж оптимізований рукою код.
Крістіан Клаузер

2
Для тих, хто пам’ятає знамениту цитату Кнута про оптимізацію, ось з чого почати. Потім, якщо ви виявите, що вам потрібні додаткові тисячні частки мілісекунди, виконайте одну з інших методик.
Джон

15

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

редагувати

Також для швидкості ви хочете ініціалізувати ємність вашого StringBuilder до довжини вхідного рядка. Це дозволить уникнути перерозподілу. Ці два способи разом дадуть вам швидкість і гнучкість.

інша редакція

Я думаю, що компілятор може оптимізувати це, але, що стосується стилю та ефективності, я рекомендую передбачити замість цього.


Для масивів forі foreachстворюють подібний код. Я не знаю про струни. Я сумніваюся, що JIT знає про масив-подібність String.
Крістіан Клаузер

1
Надіваюсь, JIT знає більше про характер рядка, схожий на масив, ніж ваш [жарт видалений]. Anders etal зробив багато роботи, оптимізуючи все про рядки в .net

Я робив це за допомогою HashSet <char> і це приблизно в 2 рази повільніше, ніж його метод. Використання bool [] ледве швидше (0,0469ms / iter v. 0,0599ms / iter), ніж версія, яку він має в OP ... з проблемою бути менш читаною.
user7116

1
Я не побачив різниці в роботі між використанням bool-масиву та int-масиву. Я б використовував масив bool, оскільки він збиває таблицю пошуку з 256 кб до 64 кб, але для такої тривіальної функції все ще багато даних ... І це лише на 30% швидше.
Guffa

1
@Guffa 2) З огляду на те, що ми зберігаємо лише буквено-цифрові символи та кілька основних латинських символів, нам потрібна лише таблиця з низьким байтом, тому розмір насправді не є проблемою. Якщо ми хотіли бути загальним призначенням, то стандартна техніка Unicode - це подвійне непряме. Іншими словами, таблиця з 256 посиланнями на таблицю, багато з яких вказують на ту саму порожню таблицю.
Стівен Судіт

12
public static string RemoveSpecialCharacters(string str)
{
    char[] buffer = new char[str.Length];
    int idx = 0;

    foreach (char c in str)
    {
        if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z')
            || (c >= 'a' && c <= 'z') || (c == '.') || (c == '_'))
        {
            buffer[idx] = c;
            idx++;
        }
    }

    return new string(buffer, 0, idx);
}

1
+1, перевірено, і це приблизно на 40% швидше, ніж StringBuilder. 0,0294ms / string v. 0,0399ms / string
user7116

Щоб переконатися, ви маєте на увазі StringBuilder з попереднім виділенням або без нього?
Стівен Судіт

З попереднім розподілом це все ще на 40% повільніше, ніж розподіл char [] та нова рядок.
user7116

2
Мені подобається це. Я foreach (char c in input.Where(c => char.IsLetterOrDigit(c) || allowedSpecialCharacters.Any(x => x == c))) buffer[idx++] = c;
переробив

11

Регулярний вираз буде виглядати так:

public string RemoveSpecialChars(string input)
{
    return Regex.Replace(input, @"[^0-9a-zA-Z\._]", string.Empty);
}

Але якщо продуктивність дуже важлива, я рекомендую вам зробити деякі орієнтири, перш ніж вибрати "шлях регулярного виведення" ...


11

Якщо ви використовуєте динамічний список символів, LINQ може запропонувати набагато швидше і витончене рішення:

public static string RemoveSpecialCharacters(string value, char[] specialCharacters)
{
    return new String(value.Except(specialCharacters).ToArray());
}

Я порівняв цей підхід проти двох попередніх "швидких" підходів (компіляція випусків):

  • Рішення масиву Char від LukeH - 427 мс
  • Рішення StringBuilder - 429 мс
  • LINQ (ця відповідь) - 98 мс

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

Якщо я перейду до жорстко розробленого рішення, використовуючи LINQ, де застереження, результати:

  • Рішення масиву Char - 7ms
  • Рішення StringBuilder - 22 мс
  • LINQ - 60 мс

Можливо, варто поглянути на LINQ або модифікований підхід, якщо ви плануєте написати більш загальне рішення, а не жорстке кодування списку символів. LINQ, безумовно, дає вам стислий, читабельний код - навіть більше, ніж Regex.


3
Цей підхід виглядає приємно, але він не працює - крім () - це встановлена ​​операція, тому ви закінчите лише першим появою кожного унікального символу в рядку.
McKenzieG1

5

Я не переконаний, що ваш алгоритм є лише ефективним. Це O (n) і дивиться на кожного символу лише один раз. Ви не отримаєте нічого кращого за це, якщо не будете магічно знати значення перед тим, як перевірити їх.

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

Побічна примітка: Перевірка A- zне є безпечною. Ви в тому числі [, \, ], ^,_ , і `...

Бічна примітка 2: Для того, щоб отримати додаткову ефективність, поставте порівняння, щоб мінімізувати кількість порівнянь. (У гіршому випадку, ви говорите 8 порівнянь тхо, тому не думайте занадто сильно.) Це змінюється з вашим очікуваним вкладом, але одним із прикладів може бути:

if (str[i] >= '0' && str[i] <= 'z' && 
    (str[i] >= 'a' || str[i] <= '9' ||  (str[i] >= 'A' && str[i] <= 'Z') || 
    str[i] == '_') || str[i] == '.')

Бічна примітка 3: Якщо з будь-якої причини вам дійсно потрібно, щоб це було швидко, оператор перемикання може бути швидшим. Компілятор повинен створити таблицю стрибків для вас, в результаті чого вийде лише одне порівняння:

switch (str[i])
{
    case '0':
    case '1':
    .
    .
    .
    case '.':
        sb.Append(str[i]);
        break;
}

1
Я згоден, що ви не можете перемогти O (n) на цьому. Однак існує вартість порівняння, яку можна знизити. Пошук таблиці має низьку, фіксовану вартість, тоді як ряд порівнянь збільшить вартість, якщо ви додасте більше винятків.
Стівен Судіт

Про сторону примітку 3, чи дійсно ви думаєте, що таблиця стрибків була б швидшою, ніж пошук таблиці?
Стівен Судіт

Я провів швидкий тест на продуктивність рішення комутатора, і він виконує те саме, що і порівняння.
Guffa

@Steven Sudit - Я б ризикнув, що вони насправді приблизно однакові. Хочете запустити тест?
лк.

7
O (n) позначення іноді мене дратують. Люди роблять дурні припущення, виходячи з того, що алгоритм вже O (n). Якщо ми змінили цю процедуру, щоб замінити виклики str [i] на функцію, яка отримала значення порівняння, побудувавши одноразове з'єднання SSL з сервером на протилежному боці світу ... ти, чорт упевнений, побачив би масову продуктивність різниця і алгоритм STILL O (n). Вартість O (1) для кожного алгоритму значна і НЕ еквівалентна!
darron


4

Ви можете використовувати звичайний вираз наступним чином:

return Regex.Replace(strIn, @"[^\w\.@-]", "", RegexOptions.None, TimeSpan.FromSeconds(1.0));

3

Мені це здається гарним. Єдине вдосконалення, яке я б зробив - це ініціалізувати StringBuilderдовжину рядка.

StringBuilder sb = new StringBuilder(str.Length);

3

Я згоден з цим зразком коду. Єдиний різний це я перетворюю на метод розширення рядкового типу. Так що ви можете використовувати його в дуже простому рядку або коді:

string test = "abc@#$123";
test.RemoveSpecialCharacters();

Дякую Гуффі за ваш експеримент.

public static class MethodExtensionHelper
    {
    public static string RemoveSpecialCharacters(this string str)
        {
            StringBuilder sb = new StringBuilder();
            foreach (char c in str)
            {
                if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
                {
                    sb.Append(c);
                }
            }
            return sb.ToString();
        }
}

2

Я б використав String Replace з регулярним виразом для пошуку "спеціальних символів", замінивши всі знайдені символи порожнім рядком.


+1, безумовно, менше коду і, можливо, більш читабельна ігнорування запису Regex один раз.
kenny

1
@kenny - я згоден. Оригінальне запитання навіть говорить про те, що рядки короткі - 10-30 символів. Але, мабуть, багато людей все ще думають, що ми продаємо час процесора другим ...
Том Бушелл

Reguler expressin працює так ліниво. Тому його не слід використовувати завжди.
RockOnGom

2

Мені довелося зробити щось подібне для роботи, але в моєму випадку мені довелося відфільтрувати все, що не є літерою, цифрою чи пробілом (але ви можете легко змінити це відповідно до своїх потреб). Фільтрація проводиться на стороні клієнта в 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

Що стосується S&G, то, що стосується Linq:

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

Я не думаю, що це буде найбільш ефективним способом.


2
Це не так, тому що це лінійний пошук.
Стівен Судіт

1
public string RemoveSpecial(string evalstr)
{
StringBuilder finalstr = new StringBuilder();
            foreach(char c in evalstr){
            int charassci = Convert.ToInt16(c);
            if (!(charassci >= 33 && charassci <= 47))// special char ???
             finalstr.append(c);
            }
return finalstr.ToString();
}

1

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

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

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

Я перевірив, і це не швидше, ніж прийнята відповідь.
Я залишу це так, як якщо б вам потрібен настроюваний набір символів, це було б хорошим рішенням.


Чому ви вважаєте, що порівняння не є O (1)?
Guffa

@Guffa Я не впевнений, що це не так, і я видалив свій коментар. І +1. Я мав би зробити більше тестування, перш ніж робити коментар.
папараццо

1

Цікаво, чи швидша заміна на основі Regex (можливо, компільована). Доведеться перевірити, що хтось виявив це приблизно в 5 разів повільніше.

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

Хороше число - це довжина початкового рядка або щось трохи нижче (залежно від характеру входів функцій).

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


Регулярний вираз уже перевірений, і він приблизно в п’ять разів повільніше. З таблицею пошуку в діапазоні 0..127 вам все одно доведеться перевірити код символів перед тим, як використовувати таблицю пошуку, оскільки символи - це 16-бітні значення, а не 7-бітні значення.
Гуффа

@Guffa Помилка ... так? ;)
Крістіан Клаузер

1

У наступному коді є такий вихід (висновок полягає в тому, що ми також можемо зберегти деякі ресурси пам'яті, виділивши масив меншого розміру):

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

Я не впевнений, що це найефективніший спосіб, але це працює для мене

 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

Відповідь справді працює, але питання було для C #. (PS: Я знаю, що це було практично п'ять років тому, але все ж ..) Я використовував Telerik VB to C # Converter, (і навпаки), і код працював чудово - хоч і не впевнений ні в кого. (Інша річ, converter.telerik.com )
Моморо

1

Тут пропонується безліч запропонованих рішень, деякі ефективніші, ніж інші, але, можливо, не дуже читабельні. Ось такий, який може бути не найефективнішим, але, безумовно, корисним для більшості ситуацій, і є досить стислим і читабельним, використовуючи 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
public static string RemoveSpecialCharacters(string str){
    return str.replaceAll("[^A-Za-z0-9_\\\\.]", "");
}

1
Боюся, replaceAllце не функція C # String, але або Java, або JavaScript
Csaba Toth,

-1
public static string RemoveAllSpecialCharacters(this string text) {
  if (string.IsNullOrEmpty(text))
    return text;

  string result = Regex.Replace(text, "[:!@#$%^&*()}{|\":?><\\[\\]\\;'/.,~]", " ");
  return result;
}

Відповідь неправильна. Якщо ви збираєтесь використовувати регулярний вираз, він повинен бути інклюзивним, а не ексклюзивним, оскільки зараз ви сумуєте за деякими символами. Насправді, вже є відповідь з регулярним виразом. І щоб бути повноцінним - регулярний вираз є СЛІЗНИМ, то пряме порівняння функцій символів.
TPAKTOPA

-3

Якщо вас турбує швидкість, скористайтеся покажчиками для редагування наявного рядка. Ви можете закріпити рядок і отримати вказівник на нього, а потім виконати цикл для кожного символу, перезаписавши кожного недійсного символу символом заміни. Це було б надзвичайно ефективно та не вимагало б виділення будь-якої нової рядкової пам'яті. Вам також потрібно буде скласти ваш модуль з небезпечною опцією та додати модифікатор "небезпечний" до заголовка методу, щоб використовувати вказівники.

static void Main(string[] args)
{
    string str = "string!$%with^&*invalid!!characters";
    Console.WriteLine( str ); //print original string
    FixMyString( str, ' ' );
    Console.WriteLine( str ); //print string again to verify that it has been modified
    Console.ReadLine(); //pause to leave command prompt open
}


public static unsafe void FixMyString( string str, char replacement_char )
{
    fixed (char* p_str = str)
    {
        char* c = p_str; //temp pointer, since p_str is read-only
        for (int i = 0; i < str.Length; i++, c++) //loop through each character in string, advancing the character pointer as well
            if (!IsValidChar(*c)) //check whether the current character is invalid
                (*c) = replacement_char; //overwrite character in existing string with replacement character
    }
}

public static bool IsValidChar( char c )
{
    return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '.' || c == '_');
    //return char.IsLetterOrDigit( c ) || c == '.' || c == '_'; //this may work as well
}

14
Noooooooooo! Зміна рядка в .NET - BAAAAAAAAAAAAD! Все в рамках покладається на правило, що струни незмінні, і якщо ви порушите, ви можете отримати дуже дивні побічні ефекти ...
Guffa
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.