Ganti Beberapa Elemen String di C #

88

Apakah ada cara yang lebih baik untuk melakukan ini ...

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

Saya telah memperluas kelas string agar tetap menjadi satu pekerjaan tetapi apakah ada cara yang lebih cepat?

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

Hanya untuk bersenang-senang (dan untuk menghentikan argumen di komentar) saya telah mendorong inti dari berbagai contoh di bawah ini.

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

Skor opsi regex sangat buruk; opsi kamus muncul paling cepat; versi panjang dari stringbuilder replace sedikit lebih cepat dari tangan pendek.

Chris McKee
sumber
1
Berdasarkan apa yang Anda miliki di tolok ukur Anda, sepertinya versi kamus tidak melakukan semua penggantian yang saya curigai adalah yang membuatnya lebih cepat daripada solusi StringBuilder.
katak
1
@toad Hi dari 2009; Saya menambahkan komentar di bawah pada bulan April tentang kesalahan mencolok itu. Intinya diperbarui meskipun saya melompati D. Versi kamus masih lebih cepat.
Chris McKee
Kemungkinan duplikat dari Alternative to String. Ganti beberapa kali?
Tot Zam
1
@TotZam setidaknya memeriksa tanggal sebelum menandai sesuatu; ini dari 2009, itu dari 2012
Chris McKee
Karena banyak jawaban di sini tampaknya berkaitan dengan kinerja, saya percaya itu harus ditunjukkan . Jawaban Andrej Adamanko kemungkinan akan menjadi yang tercepat untuk banyak pengganti; tentunya lebih cepat dari chaining .Replace () terutama pada input string yang besar seperti yang tertera pada jawabannya.
person 27

Jawaban:

125

Lebih cepat - tidak. Lebih efektif - ya, jika Anda akan menggunakan StringBuilderkelas. Dengan implementasi Anda, setiap operasi menghasilkan salinan string yang dalam keadaan dapat mengganggu kinerja. String adalah objek yang tidak dapat diubah sehingga setiap operasi hanya mengembalikan salinan yang dimodifikasi.

Jika Anda mengharapkan metode ini dipanggil secara aktif pada beberapa dengan Stringspanjang yang signifikan, mungkin lebih baik untuk "memigrasi" implementasinya ke StringBuilderkelas. Dengannya modifikasi apa pun dilakukan secara langsung pada instance itu, jadi Anda tidak perlu melakukan operasi penyalinan yang tidak perlu.

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();
    }
}
BC2
sumber
2
Untuk kejelasan jawaban kamus adalah stackoverflow
Chris McKee
3
Dalam benchmark Anda di gist.github.com/ChrisMcKee/5937656 tes kamus tidak lengkap: tes ini tidak melakukan semua penggantian dan "" menggantikan "", bukan "". Tidak semua penggantian bisa jadi alasan kenapa tercepat di benchmark. Penggantian regex juga tidak lengkap. Tetapi yang terpenting, string Anda TestData sangat pendek. Seperti status jawaban yang diterima, string harus memiliki panjang yang signifikan agar StringBuilder dapat dimanfaatkan. Bisakah Anda ulangi benchmark dengan string 10kB, 100kB dan 1MB?
Leif
Ini poin yang bagus; seperti berdiri itu digunakan untuk pembersihan url sehingga pengujian pada 100kb - 1mb tidak akan realistis. Saya akan memperbarui benchmark sehingga menggunakan semuanya, itu adalah kesalahan.
Chris McKee
Untuk kinerja terbaik, putar ulang karakter dan ganti sendiri. Namun itu bisa membosankan jika Anda memiliki lebih dari satu string karakter (temukan mereka memaksa Anda untuk membandingkan beberapa karakter sekaligus, sementara menggantinya membutuhkan alokasi lebih banyak memori dan memindahkan sisa string).
Chayim Friedman
14

Jika Anda hanya mencari solusi yang bagus dan tidak perlu menghemat beberapa nanodetik, bagaimana dengan gula LINQ?

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));
TimS
sumber
Mirip dengan contoh C di Gist (jika Anda melihat di atasnya, pernyataan linq yang lebih jelek ada di komentar)
Chris McKee
1
Menarik bahwa Anda mendefinisikan pernyataan fungsional sebagai "Lebih buruk" daripada yang prosedural.
TimS
tidak akan berdebat tentang itu; itu hanyalah preferensi. Seperti yang Anda katakan, linq hanyalah gula sintaksis; dan seperti yang saya katakan, saya sudah meletakkan yang setara di atas kode :)
Chris McKee
14

ini akan lebih efisien:

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();
    }
}
TheVillageIdiot
sumber
Sangat sulit untuk dibaca. Saya yakin Anda tahu apa yang dilakukannya tetapi seorang Dev Junior akan menggaruk kepalanya tentang apa yang sebenarnya terjadi. Saya setuju- Saya juga selalu mencari kependekan dari menulis sesuatu- Tapi itu hanya untuk kepuasan saya sendiri. Orang lain ketakutan karena tumpukan kekacauan.
Piotr Kula
3
Ini sebenarnya lebih lambat. BenchmarkOverhead ... 13ms StringClean-user151323 ... 2843ms StringClean-TheVillageIdiot ... 2921ms Bervariasi pada tayangan ulang tetapi jawabannya menang gist.github.com/anonymous/5937596
Chris McKee
11

Mungkin sedikit lebih mudah dibaca?

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

Juga tambahkan saran Baru Di Kota tentang StringBuilder ...

Paolo Tedesco
sumber
5
Ini akan lebih mudah dibaca seperti ini:private static Dictionary<string, string> _replacements = new Dictionary<string, string>() { {"&", "and"}, {",", ""}, {" ", " "} /* etc */ };
ANeves menganggap SE itu jahat
2
atau tentu saja ... private static readonly Dictionary <string, string> Replacements = new Dictionary <string, string> () {{"&", "and"}, {",", ""}, {"", ""} / * dll * /}; string statis publik Bersihkan (string ini s) {return Replacements.Keys.Aggregate (s, (current, toReplace) => current.Replace (toReplace, Replacements [toReplace])); }
Chris McKee
2
-1: Menggunakan Kamus tidak masuk akal di sini. Gunakan saja List<Tuple<string,string>>. Ini juga mengubah urutan penggantian yang diambil DAN tidak secepat mis s.Replace("a").Replace("b").Replace("c"). Jangan gunakan ini!
Thomas
6

Ada satu hal yang dapat dioptimalkan dalam solusi yang disarankan. Memiliki banyak panggilan untuk Replace()membuat kode melakukan beberapa lintasan pada string yang sama. Dengan string yang sangat panjang, solusinya mungkin lambat karena kapasitas cache CPU meleset. Mungkin salah satu harus mempertimbangkan untuk mengganti beberapa string dalam sekali jalan .

Andrej Adamenko
sumber
1
Banyak jawaban yang tampaknya mengkhawatirkan kinerja, dalam hal ini inilah yang terbaik. Dan itu sederhana karena itu hanya kelebihan String yang didokumentasikan. Ganti tempat Anda mengembalikan nilai yang diharapkan berdasarkan kecocokan, dalam contoh ini, menggunakan kamus untuk mencocokkannya. Harus sederhana untuk dipahami.
person27
4

Opsi lain yang menggunakan linq adalah

[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);
}
Luiz Felipe
sumber
Anda dapat mendeklarasikan var removeList = new List<string> { /*...*/ };lalu memanggil removeList.ForEach( /*...*/ );dan menyederhanakan kode Anda. Perhatikan juga bahwa itu tidak sepenuhnya menjawab pertanyaan karena semua string yang ditemukan diganti dengan String.Empty.
Tok
2

Saya melakukan sesuatu yang serupa, tetapi dalam kasus saya, saya melakukan serialisasi / De-serialisasi jadi saya harus bisa pergi ke dua arah. Saya menemukan menggunakan string [] [] bekerja hampir identik dengan kamus, termasuk inisialisasi, tetapi Anda bisa pergi ke arah lain juga, mengembalikan pengganti ke nilai aslinya, sesuatu yang kamus sebenarnya tidak diatur untuk dilakukan.

Edit: Anda dapat menggunakan Dictionary<Key,List<Values>>untuk mendapatkan hasil yang sama seperti string [] []

sidDemure
sumber
-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]);
}
pengguna7718176
sumber
2
Anda harus mempertimbangkan untuk menambahkan konteks pada jawaban Anda. Seperti penjelasan singkat tentang apa yang dilakukannya Dan, jika relevan, mengapa Anda menulisnya seperti yang Anda lakukan.
Neil