2009-08-24 29 views
56

Có cách nào tốt hơn để làm điều này ...Replace Multiple Elements String trong C#

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

Tôi đã kéo dài chuỗi lớp để giữ nó xuống một công việc nhưng có cách nào nhanh hơn?

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

Just for fun (và để ngăn chặn các đối số trong các ý kiến) Tôi đã xô đẩy một ý chính lên điểm chuẩn các ví dụ khác nhau dưới đây.

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

Điểm lựa chọn regex khủng khiếp; tùy chọn từ điển xuất hiện nhanh nhất; phiên bản kéo dài của trình tạo chuỗi thay thế nhanh hơn một chút so với tay ngắn.

+0

Dựa trên những gì bạn có trong tiêu chuẩn của bạn có vẻ như phiên bản từ điển không phải là làm tất cả những thay thế mà tôi nghi ngờ là những gì làm cho nó nhanh hơn các giải pháp StringBuilder. – toad

+1

@toad Xin chào từ năm 2009; Tôi đã thêm một bình luận dưới đây vào tháng Tư về sai lầm rõ ràng đó. Các gist được cập nhật mặc dù tôi bỏ qua D. Phiên bản từ điển vẫn còn nhanh hơn. –

+0

Có thể trùng lặp của [Thay thế cho String.Đặt lại nhiều lần?] (Http://stackoverflow.com/questions/12007358/alternative-to-string-replace-multiple-times) –

Trả lời

79

Nhanh hơn - không. Hiệu quả hơn - vâng, nếu bạn sử dụng lớp StringBuilder. Với việc thực hiện của bạn, mỗi thao tác tạo ra một bản sao của một chuỗi trong các trường hợp có thể làm giảm hiệu suất. Các chuỗi là bất biến đối tượng để mỗi thao tác chỉ trả về một bản sao đã sửa đổi.

Nếu bạn mong đợi phương pháp này được chủ động gọi trên nhiều Strings có độ dài đáng kể, có thể tốt hơn là "di chuyển" triển khai của nó lên lớp StringBuilder. Với nó bất kỳ sửa đổi được thực hiện trực tiếp trên trường hợp đó, vì vậy bạn phụ tùng hoạt động sao chép không cần thiết.

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

Để rõ ràng câu trả lời từ điển là http://stackoverflow.com/a/1321366/52912 nhanh nhất –

+2

Trong tiêu chuẩn của bạn trên https://gist.github.com/ChrisMcKee/5937656 kiểm tra từ điển không hoàn thành: nó không làm tất cả thay thế và "" thay thế "", không phải "". Không làm tất cả thay thế có thể là lý do, tại sao nó nhanh nhất trong điểm chuẩn. Thay thế regex cũng không hoàn thành. Nhưng quan trọng nhất là chuỗi TestData của bạn là _very_ ngắn. Giống như các trạng thái trả lời được chấp nhận, chuỗi phải có độ dài đáng kể để StringBuilder có lợi thế. Bạn có thể vui lòng lặp lại điểm chuẩn với các chuỗi 10kB, 100kB và 1MB không? – Leif

+0

Đó là một điểm tốt; vì nó đứng nó đã được sử dụng để làm sạch url để kiểm tra tại 100kb - 1mb sẽ có được không thực tế. Tôi sẽ cập nhật các điểm chuẩn để nó sử dụng toàn bộ điều mặc dù, đó là một sai lầm. –

8

này sẽ hiệu quả hơn:

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

Thật khó đọc. Tôi chắc chắn bạn biết những gì nó làm nhưng một Junior Dev sẽ gãi đầu của mình vào những gì thực sự xảy ra. Tôi đồng ý- Tôi cũng luôn luôn tìm kiếm những bàn tay rút ngắn của một cái gì đó-Nhưng nó chỉ là sự hài lòng của riêng tôi. Những người khác đang hoảng sợ với đống lộn xộn. – ppumkin

+2

Điều này thực sự chậm hơn. BenchmarkOverhead ... 13ms StringClean-user151323 ... 2843ms StringClean-TheVillageIdiot ... 2921ms Khác nhau trên chiếu lại nhưng câu trả lời thắng https://gist.github.com/anonymous/5937596 –

10

Có lẽ một chút dễ đọc hơn?

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

Ngoài ra thêm mới Trong gợi ý Town về StringBuilder ...

+4

Nó sẽ là hơn có thể đọc được như sau: 'private static Dictionary _replacements = Từ điển mới () {{" & "," và "}, {", "," "}, {" "," " }/* etc * /}; ' – ANeves

+1

hoặc tất nhiên ... riêng từ điển chỉ đọc riêng Replacements = new Dictionary () {{" & "," and "}, { ",", ""}, {"", ""}/* etc * /}; \t \t công static string sạch (điều này string s) \t \t { \t \t \t trở Replacements.Keys.Aggregate (s, (hiện tại, toReplace) => current.Replace (toReplace, Thay thế [toReplace])); \t \t} –

1

tôi đang làm một cái gì đó tương tự, nhưng trong trường hợp của tôi, tôi đang làm serialization/De-serialization vì vậy tôi cần để có thể đi cả hai hướng. Tôi thấy việc sử dụng chuỗi [] [] gần như giống với từ điển, bao gồm khởi tạo, nhưng bạn cũng có thể đi theo hướng khác, trả lại giá trị thay thế cho giá trị ban đầu của chúng, một thứ mà từ điển thực sự không được thiết lập để thực hiện.

Chỉnh sửa: Bạn có thể sử dụng Dictionary<Key,List<Values>> để có được cùng một kết quả như string [] []

4

Nếu bạn chỉ đơn giản là sau khi một giải pháp khá và không cần phải tiết kiệm một vài nano giây, làm thế nào về một số đường 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)); 
+0

Tương tự như ví dụ C trong Gist (nếu bạn nhìn ở trên nó, câu lệnh linq xấu hơn nằm trong chú thích) –

+1

Thú vị khi bạn xác định một biểu đồ chức năng là "Uglier" hơn là một thủ tục. – TimS

+0

sẽ không tranh luận về nó; chỉ là sở thích của nó. Như bạn nói, linq chỉ đơn giản là cú pháp đường; và như tôi đã nói tôi đã đặt tương đương trên mã :) –

3

Có một thứ có thể được tối ưu hóa trong các giải pháp được đề xuất. Có nhiều cuộc gọi đến Replace() làm cho mã thực hiện nhiều lần vượt qua cùng một chuỗi. Với chuỗi rất dài, các giải pháp có thể chậm vì dung lượng bộ nhớ cache CPU bị mất. Có thể là một trong những nên xem xét replacing multiple strings in a single pass.

-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]); 
} 
+1

Bạn nên cân nhắc thêm ngữ cảnh vào câu trả lời của mình. Giống như một lời giải thích ngắn gọn về những gì nó đang làm Và, nếu có liên quan, tại sao bạn đã viết nó theo cách bạn đã làm. – Neil

1

Một lựa chọn khác sử dụng LINQ là

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

Bạn có thể khai báo 'var removeList = new List {/*...*/};' sau đó chỉ cần gọi 'removeList.ForEach (/*...*/);' và đơn giản hóa mã của bạn. Cũng lưu ý rằng nó không trả lời đầy đủ câu hỏi bởi vì * tất cả * chuỗi tìm thấy được thay thế bằng 'String.Empty'. –