แทนที่องค์ประกอบสตริงหลายรายการใน C #


88

มีวิธีที่ดีกว่านี้ไหม ...

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

ฉันได้ขยายคลาสสตริงเพื่อให้มันเหลือเพียงงานเดียว แต่มีวิธีที่เร็วกว่านี้ไหม

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

เพียงเพื่อความสนุกสนาน (และเพื่อหยุดการโต้แย้งในความคิดเห็น) ฉันได้เพิ่มส่วนสำคัญในการเปรียบเทียบตัวอย่างต่างๆด้านล่าง

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

ตัวเลือก regex ทำคะแนนได้แย่มาก ตัวเลือกพจนานุกรมเกิดขึ้นเร็วที่สุด การเปลี่ยนสตริงบิลด์เดอร์รุ่นยาวจะเร็วกว่ามือสั้นเล็กน้อย


1
จากสิ่งที่คุณมีในการวัดประสิทธิภาพดูเหมือนว่าเวอร์ชันพจนานุกรมไม่ได้ทำการแทนที่ทั้งหมดซึ่งฉันสงสัยว่าเป็นสิ่งที่ทำให้เร็วกว่าโซลูชัน StringBuilder
คางคก

1
@toad สวัสดีปี 2009; ฉันได้เพิ่มความคิดเห็นด้านล่างในเดือนเมษายนเกี่ยวกับความผิดพลาดที่เห็นได้ชัดนั้น ส่วนสำคัญได้รับการอัปเดตแม้ว่าฉันจะข้ามไป D เวอร์ชันพจนานุกรมก็ยังเร็วกว่า
Chris McKee

เป็นไปได้ที่จะซ้ำกันของAlternative to String แทนที่หลาย ๆ ครั้ง?
Tot Zam

1
@TotZam อย่างน้อยตรวจสอบวันที่ก่อนที่จะตั้งค่าสถานะสิ่งต่างๆ นี่คือปี 2009 จากปี 2012
Chris McKee

เนื่องจากคำตอบมากมายในที่นี้ดูเหมือนจะเกี่ยวข้องกับประสิทธิภาพฉันเชื่อว่าคำตอบของ Andrej Adamankoน่าจะเป็นคำตอบที่เร็วที่สุดสำหรับการเปลี่ยนจำนวนมาก เร็วกว่าการล่ามโซ่อย่างแน่นอนแทนที่ () โดยเฉพาะอย่างยิ่งในสตริงอินพุตขนาดใหญ่ตามที่ระบุไว้ในคำตอบของเขา
บุคคลที่ 27

คำตอบ:


125

เร็วขึ้น - ไม่ มีประสิทธิภาพมากขึ้น - ใช่ถ้าคุณจะใช้StringBuilderคลาส การดำเนินการของคุณแต่ละครั้งจะสร้างสำเนาของสตริงซึ่งอาจทำให้ประสิทธิภาพการทำงานลดลง สตริงเป็นวัตถุที่ไม่เปลี่ยนรูปดังนั้นแต่ละการดำเนินการจะส่งคืนสำเนาที่แก้ไข

หากคุณคาดหวังว่าวิธีนี้จะถูกเรียกใช้อย่างจริงจังโดยStringsมีความยาวที่มีนัยสำคัญหลายอย่างการ "ย้าย" การนำไปใช้งานไปยังStringBuilderคลาสนั้นอาจเป็นการดีกว่า ด้วยการปรับเปลี่ยนใด ๆ จะดำเนินการโดยตรงกับอินสแตนซ์นั้นดังนั้นคุณจึงต้องสำรองการทำสำเนาที่ไม่จำเป็น

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
เพื่อความชัดเจนคำตอบจากพจนานุกรมคือstackoverflow.com/a/1321366/52912 ที่
Chris McKee

3
ในเกณฑ์มาตรฐานของคุณในgist.github.com/ChrisMcKee/5937656การทดสอบพจนานุกรมยังไม่สมบูรณ์: ไม่ได้ทำการแทนที่ทั้งหมดและ "" แทนที่ "" ไม่ใช่ "" การไม่ทำการเปลี่ยนทั้งหมดอาจเป็นเหตุผลว่าทำไมจึงเร็วที่สุดในเกณฑ์มาตรฐาน การแทนที่ regex ไม่สมบูรณ์เช่นกัน แต่ที่สำคัญที่สุดคือสตริง TestData ของคุณสั้นมาก เช่นเดียวกับสถานะคำตอบที่ยอมรับสตริงจะต้องมีความยาวมากเพื่อให้ StringBuilder ได้เปรียบ คุณช่วยทำเกณฑ์มาตรฐานซ้ำด้วยสตริง 10kB, 100kB และ 1MB ได้ไหม
Leif

เป็นจุดที่ดี ตามที่ระบุไว้ว่ามันถูกใช้สำหรับการล้าง URL ดังนั้นการทดสอบที่ 100kb - 1mb จะไม่สมจริง ฉันจะอัปเดตเกณฑ์มาตรฐานเพื่อให้ใช้ทั้งสิ่งนั้นเป็นความผิดพลาด
Chris McKee

เพื่อให้ได้ประสิทธิภาพสูงสุดให้วนซ้ำอักขระและแทนที่ด้วยตัวเอง อย่างไรก็ตามอาจเป็นเรื่องน่าเบื่อหากคุณมีสตริงอักขระมากกว่าตัวเดียว (พบว่าสตริงบังคับให้คุณเปรียบเทียบอักขระหลายตัวพร้อมกันในขณะที่การแทนที่ต้องจัดสรรหน่วยความจำเพิ่มขึ้นและย้ายสตริงที่เหลือ)
Chayim Friedman

14

หากคุณเป็นเพียงวิธีแก้ปัญหาที่สวยงามและไม่จำเป็นต้องประหยัดเพียงไม่กี่นาโนวินาทีน้ำตาล 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));

คล้ายกับตัวอย่าง C ใน Gist (ถ้าคุณดูด้านบนคำสั่ง linq ที่น่าเกลียดอยู่ในความคิดเห็น)
Chris McKee

1
น่าสนใจที่คุณให้คำจำกัดความของฟังก์ชันการทำงานเป็น "Uglier" มากกว่าขั้นตอน
TimS

จะไม่เถียงเรื่องนี้ มันเป็นเพียงความชอบเท่านั้น อย่างที่คุณพูด linq เป็นเพียงแค่น้ำตาลที่เป็นประโยค และอย่างที่ฉันบอกว่าฉันใส่รหัสที่เทียบเท่าไว้แล้ว :)
Chris McKee

14

สิ่งนี้จะมีประสิทธิภาพมากขึ้น:

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

อ่านยากจริงๆ ฉันแน่ใจว่าคุณรู้ว่ามันทำอะไร แต่นักพัฒนารุ่นเยาว์จะเกาหัวของเขาในสิ่งที่เกิดขึ้นจริง ฉันเห็นด้วย - ฉันมักจะมองหามือที่สั้นในการเขียนบางสิ่ง - แต่มันก็เพื่อความพึงพอใจของฉันเอง คนอื่น ๆ ต่างพากันคลั่งไคล้กองขยะ
Piotr Kula

3
นี่ช้ากว่าจริง BenchmarkOverhead ... 13ms StringClean-user151323 ... 2843ms StringClean-TheVillageIdiot ... 2921ms แตกต่างกันไปตามการรันซ้ำ แต่คำตอบชนะgist.github.com/anonymous/5937596
Chris McKee

11

อาจจะอ่านได้มากกว่านี้?

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

เพิ่มคำแนะนำของ New In Town เกี่ยวกับ StringBuilder ...


5
มันจะอ่านได้มากขึ้นเช่นนี้:private static Dictionary<string, string> _replacements = new Dictionary<string, string>() { {"&", "and"}, {",", ""}, {" ", " "} /* etc */ };
ANeves คิด SE เป็นความชั่วร้ายที่

2
หรือแน่นอน ... พจนานุกรมแบบอ่านอย่างเดียวแบบคงที่ส่วนตัว <string, string> Replacements = พจนานุกรมใหม่ <string, string> () {{"&", "and"}, {",", ""}, {"", ""} / * ฯลฯ * /}; สตริงคงที่สาธารณะ Clean (สตริงนี้) {return Replacements.Keys.Aggregate (s, (current, toReplace) => current.Replace (toReplace, Replacements [toReplace])); }
Chris McKee

2
-1: การใช้พจนานุกรมไม่ได้ทำให้เกิดความรู้สึกใด ๆ ที่นี่ เพียงใช้ไฟล์List<Tuple<string,string>>. นี้ยังมีการเปลี่ยนแปลงคำสั่งของ replacings s.Replace("a").Replace("b").Replace("c")จะได้รับและไม่ได้เป็นอย่างรวดเร็วเช่น อย่าใช้สิ่งนี้!
Thomas

6

มีสิ่งหนึ่งที่อาจได้รับการปรับให้เหมาะสมในโซลูชันที่แนะนำ การมีการเรียกหลายครั้งเพื่อให้Replace()รหัสทำการส่งผ่านหลายสายในสตริงเดียวกัน ด้วยสตริงที่ยาวมากการแก้ปัญหาอาจช้าเนื่องจากความจุแคชของ CPU ขาดหายไป อาจจะเป็นหนึ่งควรพิจารณาเปลี่ยนสายหลายในบัตรเดียว


1
คำตอบจำนวนมากดูเหมือนจะกังวลเกี่ยวกับประสิทธิภาพซึ่งในกรณีนี้ดีที่สุด และมันง่ายมากเพราะมันเป็นเพียงแค่สตริงที่มีเอกสารมากเกินไปแทนที่ตำแหน่งที่คุณส่งคืนค่าที่คาดไว้ตามการจับคู่ในตัวอย่างนี้โดยใช้พจนานุกรมเพื่อจับคู่ ควรเข้าใจง่าย
บุคคลที่ 27

4

อีกทางเลือกหนึ่งที่ใช้ linq คือ

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

คุณสามารถประกาศvar removeList = new List<string> { /*...*/ };จากนั้นเพียงโทรremoveList.ForEach( /*...*/ );และลดความซับซ้อนของรหัสของคุณ ยังทราบว่ามันไม่ได้อย่างเต็มที่ตอบคำถามเพราะทุกString.Emptyสตริงพบจะถูกแทนที่ด้วย
ต๊อก

2

ฉันกำลังทำบางอย่างที่คล้ายกัน แต่ในกรณีของฉันฉันกำลังทำ Serialization / De-serialization ดังนั้นฉันต้องสามารถไปทั้งสองทิศทางได้ ฉันพบว่าการใช้สตริง [] [] ทำงานได้เกือบจะเหมือนกันกับพจนานุกรมรวมถึงการเริ่มต้น แต่คุณสามารถไปในทิศทางอื่นได้เช่นกันโดยคืนค่าสิ่งทดแทนกลับเป็นค่าดั้งเดิมซึ่งเป็นสิ่งที่พจนานุกรมไม่ได้ตั้งค่าให้ทำ

แก้ไข: คุณสามารถใช้Dictionary<Key,List<Values>>เพื่อให้ได้ผลลัพธ์เช่นเดียวกับสตริง [] []


-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
คุณควรพิจารณาเพิ่มบริบทให้กับคำตอบของคุณ เช่นเดียวกับคำอธิบายสั้น ๆ ว่ามันทำอะไรและถ้าเกี่ยวข้องทำไมคุณถึงเขียนมันในแบบที่คุณทำ
Neil
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.