C # 'da Birden Çok Dize Öğesini Değiştirin


88

Bunu yapmanın daha iyi bir yolu var mı...

MyString.Trim().Replace("&", "and").Replace(",", "").Replace("  ", " ")
         .Replace(" ", "-").Replace("'", "").Replace("/", "").ToLower();

String sınıfını tek bir işe indirgemek için genişlettim ama daha hızlı bir yol var mı?

public static class StringExtension
{
    public static string clean(this string s)
    {
        return s.Replace("&", "and").Replace(",", "").Replace("  ", " ")
                .Replace(" ", "-").Replace("'", "").Replace(".", "")
                .Replace("eacute;", "é").ToLower();
    }
}

Sırf eğlence için (ve yorumlardaki argümanları durdurmak için) aşağıdaki çeşitli örnekleri karşılaştıran bir özet getirdim.

https://gist.github.com/ChrisMcKee/5937656

Normal ifade seçeneği korkunç bir puan alır; sözlük seçeneği en hızlı şekilde gelir; stringbuilder'ın uzun soluklu versiyonu, kısa elden biraz daha hızlıdır.


1
Kıyaslamalarınızda sahip olduğunuz şeye bağlı olarak, sözlük sürümü tüm değişiklikleri yapmıyor gibi görünüyor, bunu StringBuilder çözümlerinden daha hızlı yapan şey olduğundan şüpheliyim.
kurbağa

1
@toad Merhaba 2009'dan; Bu göze batan hata hakkında Nisan ayında aşağıya bir yorum ekledim. D'yi atlamış olmama rağmen öz güncellendi. Sözlük versiyonu hala daha hızlı.
Chris McKee


1
@TotZam en azından bir şeyleri işaretlemeden önce tarihleri ​​kontrol edin; Bu 2009'dan, 2012'den itibaren
Chris McKee

Buradaki pek çok cevap performansla ilgili göründüğünden, Andrej Adamanko'nun cevabının pek çok değişiklik için en hızlı olacağının belirtilmesi gerektiğine inanıyorum ; kesinlikle zincirlemeden daha hızlıdır. () özellikle yanıtında belirtildiği gibi büyük bir girdi dizgisinde.
27

Yanıtlar:


125

Daha hızlı - hayır. Daha etkili - evet, StringBuildersınıfı kullanacaksanız . Uygulamanızla her işlem, koşullar altında performansı bozabilecek bir dizenin bir kopyasını oluşturur. Dizeler değişmez nesnelerdir, bu nedenle her işlem yalnızca değiştirilmiş bir kopya döndürür.

Bu yöntemin Stringsönemli uzunlukta birden çok sayıda etkin olarak çağrılmasını bekliyorsanız , uygulamasını StringBuildersınıfa "geçirmek" daha iyi olabilir . Bununla birlikte, herhangi bir değişiklik doğrudan bu örnekte gerçekleştirilir, böylece gereksiz kopyalama işlemlerinden kurtulursunuz.

public static class StringExtention
{
    public static string clean(this string s)
    {
        StringBuilder sb = new StringBuilder (s);

        sb.Replace("&", "and");
        sb.Replace(",", "");
        sb.Replace("  ", " ");
        sb.Replace(" ", "-");
        sb.Replace("'", "");
        sb.Replace(".", "");
        sb.Replace("eacute;", "é");

        return sb.ToString().ToLower();
    }
}

2
Anlaşılır olması için, sözlüğün cevabı en hızlı stackoverflow.com/a/1321366/52912
Chris McKee

3
Gist.github.com/ChrisMcKee/5937656 adresindeki karşılaştırmanızda sözlük testi tamamlanmamıştır: tüm değiştirmeleri yapmaz ve "" yerine geçer "", "" değildir. Tüm değiştirmeleri yapmamak, kıyaslamadaki en hızlı olmasının nedeni olabilir. Normal ifade değişimi de tamamlanmadı. Ama en önemlisi TestData diziniz çok kısadır. Kabul edilen yanıt durumları gibi, StringBuilder'ın avantaj sağlayabilmesi için dizenin önemli uzunlukta olması gerekir. Lütfen karşılaştırmayı 10kB, 100kB ve 1MB dizeleriyle tekrarlayabilir misiniz?
Leif

Bu iyi bir nokta; olduğu gibi url temizliği için kullanılıyordu, bu yüzden 100kb - 1mb'deki testler gerçekçi olmayacaktı. Kıyaslamayı güncelleyeceğim, böylece her şeyi kullanması bir hataydı.
Chris McKee

En iyi performans için karakterlerin üzerinden geçin ve onları kendiniz değiştirin. Ancak, birden fazla karakter dizeniz varsa bu sıkıcı olabilir (bunları bulmak sizi aynı anda birden çok karakteri karşılaştırmaya zorlar, ancak bunları değiştirmek daha fazla bellek ayırmayı ve dizenin geri kalanını taşımayı gerektirir).
Chayim Friedman

14

Güzel bir çözüm peşindeyseniz ve birkaç nanosaniye tasarruf etmeniz gerekmiyorsa, LINQ şekerine ne dersiniz?

var input = "test1test2test3";
var replacements = new Dictionary<string, string> { { "1", "*" }, { "2", "_" }, { "3", "&" } };

var output = replacements.Aggregate(input, (current, replacement) => current.Replace(replacement.Key, replacement.Value));

Gist örnek C'ye benzer (sen ondan daha bakarsak çirkin linq deyimi yorumunda ise)
Chris McKee

1
İşlevsel bir ifadeyi prosedürel ifadeden çok "Çirkin" olarak tanımlamanız ilginç.
TimS

bunun hakkında tartışmayacak; sadece tercihidir. Dediğiniz gibi, linq basitçe sözdizimsel şekerdir; ve daha önce de söylediğim gibi eşdeğerini kodun üstüne koymuştum :)
Chris McKee

14

bu daha verimli olacak:

public static class StringExtension
{
    public static string clean(this string s)
    {
        return new StringBuilder(s)
              .Replace("&", "and")
              .Replace(",", "")
              .Replace("  ", " ")
              .Replace(" ", "-")
              .Replace("'", "")
              .Replace(".", "")
              .Replace("eacute;", "é")
              .ToString()
              .ToLower();
    }
}

Okuması gerçekten zor. Eminim ne işe yaradığını biliyorsunuzdur ama bir Junior Dev gerçekte neler olup bittiğine karşı kafasını kaşıyacaktır. Katılıyorum - ayrıca her zaman bir şeyler yazmanın kısa elini ararım - Ama bu sadece kendi memnuniyetim içindi. Diğer insanlar karmaşa yığınında çıldırdı.
Piotr Kula

3
Bu aslında daha yavaştır. BenchmarkOverhead ... 13ms StringClean-user151323 ... 2843ms StringClean-TheVillageIdiot ... 2921ms Tekrar gösterimlere göre değişir ancak cevap gist.github.com/anonymous/5937596
Chris McKee

11

Belki biraz daha okunabilir mi?

    public static class StringExtension {

        private static Dictionary<string, string> _replacements = new Dictionary<string, string>();

        static StringExtension() {
            _replacements["&"] = "and";
            _replacements[","] = "";
            _replacements["  "] = " ";
            // etc...
        }

        public static string clean(this string s) {
            foreach (string to_replace in _replacements.Keys) {
                s = s.Replace(to_replace, _replacements[to_replace]);
            }
            return s;
        }
    }

Ayrıca New In Town'ın StringBuilder hakkındaki önerisini ekleyin ...


5
Şöyle daha okunaklı olurdu:private static Dictionary<string, string> _replacements = new Dictionary<string, string>() { {"&", "and"}, {",", ""}, {" ", " "} /* etc */ };
ANeves SE'nin kötü olduğunu düşünüyor

2
veya elbette ... private static salt okunur Sözlük <string, string> Replacements = new Dictionary <string, string> () {{"&", "ve"}, {",", ""}, {"", " " } /* vb */ }; public static string Clean (bu string s) {return Replacements.Keys.Aggregate (s, (current, toReplace) => current.Replace (toReplace, Replacements [toReplace])); }
Chris McKee

2
-1: Sözlük kullanmak burada bir anlam ifade etmez. Sadece bir List<Tuple<string,string>>. Bu aynı zamanda değiştirmelerin sırasını da değiştirir VE örn s.Replace("a").Replace("b").Replace("c"). Kadar hızlı değildir . Bunu kullanma!
Thomas

6

Önerilen çözümlerde optimize edilebilecek bir şey var. Çok sayıda çağrıya sahip olmak Replace(), kodun aynı dizede birden çok geçiş yapmasını sağlar. Çok uzun dizelerde, CPU önbellek kapasitesi kayıpları nedeniyle çözümler yavaş olabilir. Tek geçişte birden çok dizeyi değiştirmeyi düşünmek gerekebilir .


1
Pek çok cevap performansla ilgili görünüyor, bu durumda en iyisi bu. Ve bu basit çünkü bu sadece belgelenmiş bir String aşırı yüklemesi . Bu örnekte, eşleşmeye dayalı olarak beklenen bir değeri döndürdüğünüz yeri değiştirin, bunları eşleştirmek için bir sözlük kullanın. Anlaşılması basit olmalı.
27

4

Linq kullanan başka bir seçenek de

[TestMethod]
public void Test()
{
  var input = "it's worth a lot of money, if you can find a buyer.";
  var expected = "its worth a lot of money if you can find a buyer";
  var removeList = new string[] { ".", ",", "'" };
  var result = input;

  removeList.ToList().ForEach(o => result = result.Replace(o, string.Empty));

  Assert.AreEqual(expected, result);
}

Daha var removeList = new List<string> { /*...*/ };sonra removeList.ForEach( /*...*/ );kodunuzu arayabilir ve basitleştirebilirsiniz. Ayrıca, bulunan tüm dizeler ile değiştirildiği için soruyu tam olarak yanıtlamadığını da unutmayın String.Empty.
Tok '

2

Benzer bir şey yapıyorum, ancak benim durumumda serileştirme / serileştirme yapıyorum, bu yüzden her iki yöne de gidebilmem gerekiyor. Bir [] [] dizesi kullanmanın sözlükle neredeyse aynı şekilde çalıştığını görüyorum, buna başlatma dahil, ama siz de diğer yöne gidebilir, ikameleri orijinal değerlerine geri döndürebilirsiniz, bu sözlüğün gerçekten yapamayacağı bir şeydir.

Düzenle: Dictionary<Key,List<Values>>[] [] dizesi ile aynı sonucu elde etmek için kullanabilirsiniz


-1
string input = "it's worth a lot of money, if you can find a buyer.";
for (dynamic i = 0, repl = new string[,] { { "'", "''" }, { "money", "$" }, { "find", "locate" } }; i < repl.Length / 2; i++) {
    input = input.Replace(repl[i, 0], repl[i, 1]);
}

2
Cevaplarınıza bağlam eklemeyi düşünmelisiniz. Mesela ne yaptığına dair kısa bir açıklama ve ilgiliyse, neden bunu yaptığınız gibi yazdınız.
Neil
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.