Dizeden özel karakterleri kaldırmanın en etkili yolu


266

Bir dizeden tüm özel karakterleri kaldırmak istiyorum. İzin verilen karakterler AZ (büyük harf veya küçük harf), rakamlar (0-9), alt çizgi (_) veya nokta işaretidir (.).

Aşağıdakiler var, işe yarıyor ama çok verimli olmadığından şüpheliyim (biliyorum!):

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

Bunu yapmanın en etkili yolu nedir? Normal ifade nasıl görünür ve normal dize manipülasyonu ile nasıl karşılaştırılır?

Temizlenecek dizeler oldukça kısa, genellikle 10 ila 30 karakter uzunluğunda olacaktır.


5
Daha etkili olmayacağı için bunu bir cevaba koymayacağım, ancak en azından daha okunaklı hale getirmek için if ifadenizde kullanabileceğiniz char.IsLetterOrDigit () gibi bir dizi statik char yöntemi var.
Martin Harris

5
A'dan Z'ye kontrol etmenin güvenli olduğundan emin değilim, çünkü alfabetik olmayan 6 karakter getiriyor, sadece biri isteniyor (alt çubuk).
Steven Sudit

4
Kodunuzu daha okunaklı hale getirmeye odaklanın. Bunu saniyede 500 kez gibi bir döngüde yapmazsanız, verimlilik çok önemli değildir. Bir normal ifade kullanın ve okumak çok daha kolay olacak. L
Byron Whitlock

4
Byron, muhtemelen okunabilirliği vurgulamak konusunda haklısın. Ancak, normal ifadenin okunabilir olması konusunda şüpheliyim. :-)
Steven Sudit

2
Düzenli ifadeler okunabilir olsun ya da olmasın, Almanca'nın okunabilir olması gibi bir şeydir; bunu bilip bilmediğinize bağlıdır (her iki durumda da şimdi ve sonra mantıklı olmayan gramer kurallarına rastlayacaksınız;)
Blixt

Yanıtlar:


325

Neden yönteminizin etkili olmadığını düşünüyorsunuz? Aslında bunu yapmanın en etkili yollarından biri.

Elbette karakteri yerel bir değişkene okumalısınız veya dizi erişim sayısını azaltmak için bir numaralandırıcı kullanmalısınız:

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

Böyle bir yöntemi etkili kılan bir şey, iyi ölçeklendirilmesidir. Yürütme süresi dizenin uzunluğuna göreceli olacaktır. Büyük bir ipte kullanacak olursanız kötü bir sürpriz olmaz.

Düzenleme:
Her karakter bir 24 karakter dizesi ile milyon kez çalışan hızlı performans testi yaptım. Bunlar sonuçlar:

Orijinal fonksiyon: 54.5 ms.
Önerilen değişiklikim: 47,1 ms.
StringBuilder kapasitesine sahip maden: 43.3 ms.
Normal ifade: 294.4 ms.

Edit 2: Yukarıdaki kodda AZ ve az arasındaki farkı ekledim. (Performans testini tekrar ediyorum ve gözle görülür bir fark yok.)

Düzenleme 3:
Arama + char [] çözümünü test ettim ve yaklaşık 13 msn'de çalışıyor.

Ödenecek fiyat, elbette, büyük arama tablosunun başlatılması ve hafızada tutulmasıdır. O kadar çok veri değil, ama bu kadar önemsiz bir işlev için çok fazla ...

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
Katılıyorum. Yapacağım diğer tek değişiklik ilk kapasite bağımsız değişkenini StringBuilder yapıcısına "= new StringBuilder (str.Length)" eklemektir.
David

2
Benim testime char[]göre StringBuilder, bunun yerine tampon kullanarak cevabımın hafif bir kenarı var. (Benimki daha az okunabilir, bu nedenle küçük performans avantajı muhtemelen buna değmez.)
LukeH

1
@Steven: Durum böyle olabilir, ancak kriterler kendileri için konuşuyor! Testlerimde, char[]arabellek kullanmak StringBuilder, on binlerce karakter uzunluğundaki dizelere ölçeklenirken bile (biraz) daha iyi performans gösterir .
LukeH

10
@downvoter: Neden inişli çıkışlı? Eğer neyin yanlış olduğunu düşündüğünüzü açıklamazsanız, cevabı geliştiremezsiniz.
Guffa

2
@SILENT: Hayır, değil, ama bunu sadece bir kez yapmalısın. Yöntemi her çağırdığınızda (ve yöntemi sık sık çağırırsanız) büyük bir dizi ayırırsanız, yöntem açık ara en yavaş olur ve çöp toplayıcı için çok fazla çalışmaya neden olur.
Guffa

195

Performansı işlevinizden gerçekten çıkarmanız gerekmedikçe, bakımı ve anlaşılması en kolay olanla devam edin. Normal bir ifade şöyle görünür:

Ek performans için, önceden derleyebilir veya sadece ilk çağrıda derlemesini söyleyebilirsiniz (sonraki çağrılar daha hızlı olacaktır.)

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

1
Bunun muhtemelen OP'nin yaklaşımından daha hızlı olacağı, özellikle de önceden derlenmişse, yeterince karmaşık bir sorgu olduğunu tahmin ediyorum. Ancak bunu destekleyecek hiçbir kanıtım yok. Test edilmelidir. Çok daha yavaş olmadıkça, bu yaklaşımı ne olursa olsun seçerim, çünkü okunması ve bakımı daha kolaydır. +1
rmeador

6
Onun çok basit bir regex (hiçbir backtracking ya da orada herhangi bir karmaşık şeyler) bu yüzden oldukça hızlı olmalı.

9
@ rmeador: derlenmeden yaklaşık 5 kat daha yavaş, derlenmiş olarak yönteminden 3 kat daha yavaştır. Yine de 10x daha basit :-D
user7116

6
Düzenli ifadeler sihirli bir çekiç değildir ve el ile optimize edilmiş koddan daha hızlı değildir.
Christian Klauser

2
Knuth'un optimizasyon hakkındaki ünlü sözünü hatırlayanlar için, burası nereden başlamalı. Ardından, milisaniyelik bir performansın binde birine ihtiyacınız olduğunu fark ederseniz, diğer tekniklerden biriyle devam edin.
John

15

Herhangi bir karakter kombinasyonunu geçerli olarak ayarlamak için statik yapıcıda başlatabileceğiniz basit bir arama tablosu oluşturmanızı öneririm. Bu, hızlı ve tek bir kontrol yapmanızı sağlar.

Düzenle

Ayrıca, hız için StringBuilder'ınızın kapasitesini giriş dizenizin uzunluğuna başlatmak istersiniz. Bu yeniden tahsisleri önleyecektir. Bu iki yöntem birlikte size hem hız hem de esneklik sağlayacaktır.

başka bir düzenleme

Derleyici bunu optimize edebilir, ancak bir stil ve verimlilik meselesi olarak, foreach yerine foreach öneriyorum.


Diziler için, forve foreachbenzer bir kod üretir. Ama dizeleri bilmiyorum. JIT dize dizi benzeri doğası hakkında bilmek şüpheliyim.
Christian Klauser

1
Bahse girerim JIT dize dizisi gibi doğa hakkında [şaka kaldırıldı] daha fazla bilir. Anders

Bunu HashSet <char> kullanarak yaptım ve yönteminden yaklaşık 2 kat daha yavaş. Bool [] kullanmak OP'de sahip olduğu sürümden çok daha hızlıdır (0.0469ms / iter v. 0.0559ms / iter) ve daha az okunabilir olma sorunu.
user7116

1
Bir bool dizi ve int dizi kullanma arasında herhangi bir performans farkı göremedim. Arama tablosunu 256 kb'den 64 kb'ye indirdiği için bir bool dizisi kullanırdım, ancak bu kadar önemsiz bir işlev için hala çok fazla veri var ... Ve sadece yaklaşık% 30 daha hızlı.
Guffa

1
@Guffa 2) Sadece alfasayısal karakterleri ve birkaç Temel Latin karakterini tuttuğumuz göz önüne alındığında, sadece düşük bayt için bir tabloya ihtiyacımız var, bu yüzden boyut gerçekten bir sorun değil. Genel amaçlı olmak istiyorsak, standart Unicode tekniği çift dolaylı yöntemdir. Başka bir deyişle, çoğu aynı boş tabloya işaret eden 256 tablo referansı içeren bir tablo.
Steven Sudit

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, test edildi ve StringBuilder'dan yaklaşık% 40 daha hızlı. 0.0294ms / string / 0.0399ms / string
user7116

Sadece emin olmak için, ön ayırmalı veya ön ayırmasız StringBuilder mi demek istediniz?
Steven Sudit

Ön ayırma ile, char [] ayırmasından ve yeni dizgiden hala% 40 daha yavaştır.
user7116

2
Bunu severim. Bu yöntemi değiştirdimforeach (char c in input.Where(c => char.IsLetterOrDigit(c) || allowedSpecialCharacters.Any(x => x == c))) buffer[idx++] = c;
Chris Marisic

11

Normal bir ifade şöyle görünecektir:

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

Ancak performans çok önemliyse, "normal ifade yolunu" seçmeden önce bazı karşılaştırmalar yapmanızı öneririm ...


11

Dinamik bir karakter listesi kullanıyorsanız, LINQ çok daha hızlı ve zarif bir çözüm sunabilir:

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

Bu yaklaşımı önceki "hızlı" yaklaşımlardan ikisiyle karşılaştırdım (sürüm derleme):

  • LukeH tarafından Char dizi çözümü - 427 ms
  • StringBuilder çözümü - 429 ms
  • LINQ (bu cevap) - 98 ms

Algoritmanın biraz değiştirildiğine dikkat edin - karakterler, sabit kodlu yerine bir dizi olarak iletilir, bu da işleri biraz etkileyebilir (yani / diğer çözümlerin karakter dizisini kontrol etmek için bir iç kat döngüsü vardır).

Ben bir LINQ nerede yan tümcesi kullanarak sabit kodlu bir çözüm geçmek, sonuçları şunlardır:

  • Char dizi çözümü - 7 ms
  • StringBuilder çözümü - 22ms
  • LINQ - 60 ms

Karakter listesini kodlamak yerine daha genel bir çözüm yazmayı planlıyorsanız LINQ veya değiştirilmiş bir yaklaşıma bakmaya değer olabilir. LINQ kesinlikle size kısa ve okunaklı bir kod sunar - Regex'ten bile daha fazlası.


3
Bu yaklaşım hoş görünüyor, ancak çalışmıyor - Except () ayarlanmış bir işlemdir, bu nedenle dizedeki her benzersiz karakterin yalnızca ilk görünümü ile sonuçlanırsınız.
McKenzieG1

5

Algoritmanın etkili bir şey olduğuna ikna olmadım. O (n) ve her karaktere sadece bir kez bakar. Değerleri kontrol etmeden önce sihirli bir şekilde bilmediğiniz sürece bundan daha iyisi elde edemezsiniz.

Ancak senin StringBuilderdize başlangıç ​​boyutu için kapasitesini başlatmak istiyorum . Sanırım algılanan performans sorununuz bellek yeniden tahsisinden kaynaklanıyor.

Yan not: Kontrol A- zgüvenli değildir. Sen dahil ediyoruz [, \, ], ^, _, ve `...

Yan not 2: Bu ekstra verimlilik için, karşılaştırma sayısını en aza indirgemek amacıyla karşılaştırmaları sıralayın. (En kötü ihtimalle, 8 karşılaştırma ile konuşuyorsunuz, bu yüzden çok fazla düşünmeyin.) Bu, beklenen girdinizle değişir, ancak bir örnek olabilir:

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

Yan not 3: Herhangi bir nedenle GERÇEKTEN hızlı olması gerekiyorsa, bir anahtar deyimi daha hızlı olabilir. Derleyici sizin için bir atlama tablosu oluşturmalı ve bu da yalnızca tek bir karşılaştırma ile sonuçlanmalıdır:

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

1
Bu konuda O (n) 'yu yenemeyeceğinizi kabul ediyorum. Ancak, karşılaştırılabilecek bir maliyet düşürülebilir. Bir tablo aramasının düşük, sabit bir maliyeti vardır, ancak daha fazla istisna ekledikçe bir dizi karşılaştırma maliyette artacaktır.
Steven Sudit

Yan not 3 hakkında, gerçekten atlama tablosunun tablo aramasından daha hızlı olacağını düşünüyor musunuz?
Steven Sudit

Hızlı çözüm testini anahtar çözümü üzerinde çalıştırdım ve karşılaştırmayla aynı şeyi yapıyor.
Guffa

@Steven Sudit - Aslında aynı olduklarını düşünürdüm. Test yapmak ister misiniz?
Ic.

7
O (n) notasyonu bazen beni kızdırıyor. İnsanlar, algoritmanın zaten O (n) olduğu gerçeğine dayanarak aptal varsayımlar yapacaklardır. Bu rutini str [i] çağrılarını, dünyanın karşı tarafındaki bir sunucu ile bir kerelik SSL bağlantısı oluşturarak karşılaştırma değerini alan bir işlevle değiştirmek için değiştirdiysek ... ve algoritma STILL O (n) 'dir. Her algoritma için O (1) maliyeti önemlidir ve eşdeğer DEĞİLDİR!
darron

4
StringBuilder sb = new StringBuilder();

for (int i = 0; i < fName.Length; i++)
{
   if (char.IsLetterOrDigit(fName[i]))
    {
       sb.Append(fName[i]);
    }
}

4

Düzenli ifadeyi aşağıdaki gibi kullanabilirsiniz:

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

3

Bana iyi geliyor. Yapacağım tek gelişme StringBuilderdizginin uzunluğuyla başlatmaktır .

StringBuilder sb = new StringBuilder(str.Length);

3

Bu kod örneğini kabul ediyorum. Sadece farklı ben dize türü Uzantı Yöntemi içine yapmak. Böylece çok basit bir satırda veya kodda kullanabilirsiniz:

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

Denemeniz için Guffa'ya teşekkür ederiz.

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

Boş bir dize ile bulunan tüm karakterleri değiştirerek, "özel karakterler" için arama yapan bir Normal İfade ile Dize Değiştirme kullanırdım.


Bir kez yazma Regex kesinlikle daha az kod ve tartışmalı olarak daha okunabilir.
kenny

1
@kenny - Kabul ediyorum. Orijinal soru bile dizelerin kısa olduğunu belirtir - 10-30 karakter. Ama görünüşe göre bir çok insan hala CPU zamanını ikinci sırada sattığımızı düşünüyor ...
Tom Bushell

Reguler expressin çok tembel çalışır, bu yüzden her zaman kullanılmamalıdır.
RockOnGom

2

İş için benzer bir şey yapmak zorunda kaldım, ancak benim durumumda bir harf, sayı veya boşluk olmayan her şeyi filtrelemek zorunda kaldım (ancak ihtiyaçlarınıza göre kolayca değiştirebilirsiniz). Filtreleme, istemci tarafında JavaScript ile yapılır, ancak güvenlik nedeniyle, filtreleme sunucu tarafı da yapıyorum. Dizelerin çoğunun temiz olmasını beklediğimden, gerçekten gerekmedikçe dizeyi kopyalamaktan kaçınmak istiyorum. Bu, hem temiz hem de kirli dizeler için daha iyi performans göstermesi gereken aşağıdaki uygulamaya izin ver.

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'ler için, Linq ified yolu:

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

Ancak bunun en etkili yol olacağını düşünmüyorum.


2
Değil, çünkü doğrusal bir arama.
Steven Sudit

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

kullanın:

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
}

Ve temiz bir ip alacaksın s .

erase()tüm özel karakterleri sıyırır ve my_predicate()işlevi ile son derece özelleştirilebilir .


1

HashSet O (1)
Mevcut karşılaştırmadan daha hızlı olduğundan emin değilim

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

Test ettim ve bu kabul edilen cevaptan daha hızlı değil.
Yapılandırılabilir bir karakter setine ihtiyacınız varmış gibi bırakacağım, bu iyi bir çözüm olacaktır.


Neden karşılaştırmanın O (1) olmadığını düşünüyorsunuz?
Guffa

@Guffa Emin değilim ve yorumumu kaldırdım. Ve +1. Yorum yapmadan önce daha fazla test yapmalıydım.
paparazzo

1

Regex tabanlı bir yedek (muhtemelen derlenmiş) daha hızlı olup olmadığını merak ediyorum. Bunu test etmek zorunda kalırdımBirisi bu ~ 5 kat daha yavaş bulduk etmek gerekir.

Bunun dışında, StringBuilder'ı beklenen uzunlukta başlatmanız gerekir, böylece ara dizenin büyüdüğü sırada kopyalanması gerekmez.

İyi bir sayı, orijinal dizenin uzunluğudur veya biraz daha düşük bir şeydir (işlev girişlerinin doğasına bağlı olarak).

Son olarak, bir karakterin kabul edilip edilmeyeceğini öğrenmek için bir arama tablosu (0..127 aralığında) kullanabilirsiniz.


Düzenli bir ifade zaten test edilmiştir ve yaklaşık beş kat daha yavaştır. 0..127 aralığında bir arama tablosu ile, arama tablosunu kullanmadan önce karakter kodunu kontrol etmelisiniz, çünkü karakterler 7 bit değerleri değil 16 bit değerleridir.
Guffa

@Guffa Err ... evet? ;)
Christian Klauser

1

Aşağıdaki kod aşağıdaki çıktıya sahiptir (sonuç, dizi daha küçük boyut ayıran bazı bellek kaynaklarını da kaydedebileceğimizdir):

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  

Rusça yerel ayarı desteklemek için aşağıdaki kod satırlarını da ekleyebilirsiniz (dizi boyutu 1104 olacaktır):

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

En etkili yol olduğundan emin değilim, ama benim için çalışıyor

 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

Cevap yapar işi, ama soru içindi C #. (PS: Bunun neredeyse beş yıl önce olduğunu biliyorum, ama yine de ..) Telerik VB'den C # Dönüştürücüye kullandım, (Ve tersi) ve kod gayet iyi çalıştı - başka kimseden emin değilim. (Başka bir şey, converter.telerik.com )
Momoro

1

Burada, bazıları diğerlerinden daha verimli, ancak belki de çok okunabilir olmayan birçok önerilen çözüm var. İşte en verimli olmayan, ancak çoğu durum için kesinlikle kullanılabilir olan ve oldukça özlü ve okunabilir olan, Linq'i kullanan:

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
Korkarım replaceAllC # String işlevi değil, Java veya JavaScript
Csaba Toth

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

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

Cevap yanlış. Normal ifadeyi kullanacaksanız, özel değil, kapsayıcı olmalı, çünkü şimdi bazı karakterleri kaçırıyorsunuz. Aslında, regex ile zaten bir cevap var. Ve tam olması - regex daha sonra doğrudan chars fonksiyonu karşılaştırmak için YAVAŞ olduğunu.
TPAKTOPA

-3

Hız konusunda endişeleriniz varsa, mevcut dizeyi düzenlemek için işaretçileri kullanın. Dizeyi sabitleyebilir ve ona bir işaretçi getirebilir, ardından her bir karakterin üzerine bir for döngüsü çalıştırabilir, her geçersiz karakterin yerine bir yedek karakter yazabilirsiniz. Son derece verimli olur ve yeni bir dize belleği tahsis edilmesini gerektirmez. Ayrıca, modülünüzü güvensiz seçenekle derlemeniz ve işaretçileri kullanabilmek için "güvensiz" değiştiriciyi yöntem başlığınıza eklemeniz gerekir.

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'de bir dizeyi değiştirmek BAAAAAAAAAAAAD! Çerçevedeki her şey, dizelerin değişmez olduğu kuralına dayanır ve eğer
kırırsanız
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.