2008-10-28 40 views
281

Tôi cần phải tìm kiếm một chuỗi và thay thế tất cả các lần xuất hiện của %FirstName%%PolicyAmount% bằng một giá trị được lấy từ cơ sở dữ liệu. Vấn đề là viết hoa của FirstName khác nhau. Điều đó ngăn cản tôi sử dụng phương thức String.Replace(). Tôi đã nhìn thấy các trang web về đề tài này mà đề nghịCó cách nào thay thế cho chuỗi.Địa chỉ không phân biệt chữ hoa chữ thường không?

Regex.Replace(strInput, strToken, strReplaceWith, RegexOptions.IgnoreCase); 

Tuy nhiên đối với một số lý do khi tôi cố gắng và thay thế %PolicyAmount% với $0, việc thay đổi không bao giờ diễn ra. Tôi cho rằng nó có một cái gì đó để làm với ký hiệu đô la là một ký tự dành riêng trong regex.

Có phương pháp nào khác mà tôi có thể sử dụng không liên quan đến việc khử trùng đầu vào để xử lý các ký tự đặc biệt regex không?

+1

Nếu "$ 0" là biến đi trong không ảnh hưởng đến regex. – cfeduke

Trả lời

125

From MSDN
$ 0 - "Thay thế chuỗi con cuối cùng khớp với số nhóm (thập phân)".

Trong .NET Cụm từ thông dụng 0 luôn là toàn bộ kết quả khớp. Đối với một $ đen bạn cần phải

string value = Regex.Replace("%PolicyAmount%", "%PolicyAmount%", @"$$0", RegexOptions.IgnoreCase); 
+15

trong trường hợp cụ thể này, nhưng trong trường hợp các chuỗi được nhập từ bên ngoài, không thể chắc chắn rằng chúng không chứa các ký tự. – Allanrbo

+23

Bạn nên thoát khỏi các ký tự đặc biệt như sau: string value = Regex.Replace ("% PolicyAmount%", Regex.Escape ("% PolicyAmount%"), Regex.Escape ("$ 0"), RegexOptions.IgnoreCase); –

+0

Trên thực tế, việc thoát regex chuỗi thứ hai sẽ không có tác dụng ngoài việc nhận thêm \ trước khi thay thế. Để bỏ qua các ký tự đặc biệt trong chuỗi thay thế, bạn nên viết một trình so khớp khớp trả về chuỗi đó. –

0
Regex.Replace(strInput, strToken.Replace("$", "[$]"), strReplaceWith, RegexOptions.IgnoreCase); 
+3

Điều này không hoạt động. $ Không có trong mã thông báo. Đó là trong strReplace Với chuỗi. – Aheho

+9

Và bạn không thể điều chỉnh nó cho điều đó? –

+16

Trang web này được cho là kho lưu trữ các câu trả lời đúng. Không phải câu trả lời gần như chính xác. – Aheho

0

Phương thức biểu thức chính quy sẽ hoạt động. Tuy nhiên những gì bạn cũng có thể làm là thấp hơn trường hợp chuỗi từ cơ sở dữ liệu, thấp hơn trường hợp% biến% bạn có, và sau đó xác định vị trí và độ dài trong chuỗi cased thấp hơn từ cơ sở dữ liệu. Hãy nhớ rằng, các vị trí trong một chuỗi không thay đổi chỉ vì vỏ của nó thấp hơn.

Sau đó sử dụng vòng lặp ngược lại (dễ dàng hơn nếu bạn không phải tính số lần di chuyển của các điểm sau) loại bỏ chuỗi chuỗi không thấp từ cơ sở dữ liệu% biến% theo vị trí và độ dài của chúng và chèn các giá trị thay thế.

+0

Ngược lại, tôi có nghĩa là xử lý các vị trí tìm thấy ngược lại từ xa nhất đến ngắn nhất, không đi qua chuỗi từ cơ sở dữ liệu ngược lại. – cfeduke

+0

Bạn có thể, hoặc bạn chỉ có thể sử dụng Regex :) – Ray

285

Có vẻ như string.Replace nên có một tình trạng quá tải mà phải mất một lập luận StringComparison. Vì nó không, bạn có thể thử một cái gì đó như thế này:

public static string ReplaceString(string str, string oldValue, string newValue, StringComparison comparison) 
{ 
    StringBuilder sb = new StringBuilder(); 

    int previousIndex = 0; 
    int index = str.IndexOf(oldValue, comparison); 
    while (index != -1) 
    { 
     sb.Append(str.Substring(previousIndex, index - previousIndex)); 
     sb.Append(newValue); 
     index += oldValue.Length; 

     previousIndex = index; 
     index = str.IndexOf(oldValue, index, comparison); 
    } 
    sb.Append(str.Substring(previousIndex)); 

    return sb.ToString(); 
} 
+1

Phương pháp mở rộng chỉ hoạt động trong 3+ phải không? +1 Tất cả như nhau, vì OP không cụ thể, nhưng bạn có thể muốn đề cập đến nó –

+3

Ngoài ra, điều này sẽ nhanh hơn regex. –

+8

Tuyệt. Tôi sẽ thay đổi 'ReplaceString' thành' Replace'. – AMissico

29

Có vẻ phương pháp đơn giản nhất chỉ đơn giản là sử dụng các phương pháp mà tàu với Net và đã được khoảng từ Net 1.0 Thay thế:

string res = Microsoft.VisualBasic.Strings.Replace(res, 
            "%PolicyAmount%", 
            "$0", 
            Compare: Microsoft.VisualBasic.CompareMethod.Text); 

Để sử dụng phương pháp này, bạn phải thêm Tham chiếu vào tập hợp Microsoft.VisualBasic. Đây là một phần tiêu chuẩn của thời gian chạy .Net, nó không phải là một tải xuống thêm hoặc đánh dấu là lỗi thời.

+4

Nó hoạt động. Bạn cần phải thêm một tham chiếu đến hội đồng Microsoft.VisualBasic. –

+0

Lạ lẫm rằng phương pháp này có một số vấn đề khi tôi sử dụng nó (các ký tự ở đầu dòng bị thiếu). Câu trả lời phổ biến nhất ở đây từ 'C. Dragon 76' hoạt động như mong đợi. –

+1

Vấn đề với điều này là nó trả về một chuỗi mới ngay cả khi một thay thế không được thực hiện, nơi string.replace() trả về một con trỏ đến cùng một chuỗi. Có thể không hiệu quả nếu bạn đang làm một cái gì đó giống như một lá thư mẫu hợp nhất. – Brain2000

2

một phiên bản tương tự như C. Dragon, nhưng vì nếu bạn chỉ cần một sự thay thế duy nhất:

int n = myText.IndexOf(oldValue, System.StringComparison.InvariantCultureIgnoreCase); 
if (n >= 0) 
{ 
    myText = myText.Substring(0, n) 
     + newValue 
     + myText.Substring(n + oldValue.Length); 
} 
+2

Điều này sẽ không hoạt động nếu bạn cần thay thế nhiều kết quả phù hợp – hspain

29

Dưới đây là một phương pháp khuyến nông. Không chắc tôi đã tìm thấy nó ở đâu.

public static class StringExtensions 
{ 
    public static string Replace(this string originalString, string oldValue, string newValue, StringComparison comparisonType) 
    { 
     int startIndex = 0; 
     while (true) 
     { 
      startIndex = originalString.IndexOf(oldValue, startIndex, comparisonType); 
      if (startIndex == -1) 
       break; 

      originalString = originalString.Substring(0, startIndex) + newValue + originalString.Substring(startIndex + oldValue.Length); 

      startIndex += newValue.Length; 
     } 

     return originalString; 
    } 

} 
+0

Điều gì về http://stackoverflow.com/a/244933/206730? đó là cách tốt hơn? – Kiquenet

+0

Bạn có thể cần xử lý các trường hợp chuỗi rỗng/rỗng. – Vad

+2

Lỗi đa số trong giải pháp này: 1. Kiểm tra originalString, oldValue và newValue for null. 2. Không cung cấp cho hàm orginalString (không hoạt động, các kiểu đơn giản không được chuyển qua tham chiếu), nhưng gán giá trị của orginalValue trước tiên cho một chuỗi mới và sửa đổi nó và trả về nó. – RWC

9
/// <summary> 
    /// A case insenstive replace function. 
    /// </summary> 
    /// <param name="originalString">The string to examine.(HayStack)</param> 
    /// <param name="oldValue">The value to replace.(Needle)</param> 
    /// <param name="newValue">The new value to be inserted</param> 
    /// <returns>A string</returns> 
    public static string CaseInsenstiveReplace(string originalString, string oldValue, string newValue) 
    { 
     Regex regEx = new Regex(oldValue, 
      RegexOptions.IgnoreCase | RegexOptions.Multiline); 
     return regEx.Replace(originalString, newValue); 
    } 
+0

Cách nào tốt hơn? http://stackoverflow.com/a/244933/206730 thì sao? hiệu suất tốt hơn? – Kiquenet

29

Kind của một nhóm khó hiểu của câu trả lời, một phần vì tiêu đề của câu hỏi thực sự là nhiều lớn hơn so với câu hỏi cụ thể được yêu cầu. Sau khi đọc qua, tôi không chắc chắn bất kỳ câu trả lời là một vài chỉnh sửa đi từ đồng hóa tất cả những thứ tốt ở đây, vì vậy tôi figured tôi muốn cố gắng để tổng hợp.

Dưới đây là một phương pháp tiện ích mở rộng mà tôi cho rằng tránh các cạm bẫy được đề cập ở đây và cung cấp giải pháp áp dụng rộng rãi nhất.

public static string ReplaceCaseInsensitiveFind(this string str, string findMe, 
    string newValue) 
{ 
    return Regex.Replace(str, 
     Regex.Escape(findMe), 
     Regex.Replace(newValue, "\\$[0-9]+", @"$$$0"), 
     RegexOptions.IgnoreCase); 
} 

Vậy ...

  • Đây là an extension method @MarkRobinson
  • này doesn't try to skip Regex @Helge (bạn thực sự phải làm byte-by-byte nếu bạn muốn chuỗi đánh hơi như bên ngoài này Regex)
  • Vượt qua @MichaelLiu 's excellent test case, "œ".ReplaceCaseInsensitiveFind("oe", ""), mặc dù anh ấy có thể có hành vi hơi khác một chút trong đầu.

Thật không may, @HA 's comment that you have to Escape all three isn't correct. Giá trị ban đầu và newValue không cần thiết.

Lưu ý: Bạn làm, tuy nhiên, phải thoát khỏi $ s trong giá trị mới mà bạn đang chèn nếu chúng là một phần của những gì sẽ xuất hiện như một "giá trị bắt" đánh dấu. Vì vậy, ba dấu đô la trong Regex.Replace bên trong Regex.Replace [sic]. Nếu không có điều đó, một cái gì đó như thế này phá vỡ ...

"This is HIS fork, hIs spoon, hissssssss knife.".ReplaceCaseInsensitiveFind("his", @"he$0r")

Dưới đây là các lỗi:

An unhandled exception of type 'System.ArgumentException' occurred in System.dll 

Additional information: parsing "The\hisr\ is\ he\HISr\ fork,\ he\hIsr\ spoon,\ he\hisrsssssss\ knife\." - Unrecognized escape sequence \h. 

Giới thiệu với bạn những gì, tôi biết folks rằng cảm thấy thoải mái với Regex có cảm giác như họ sử dụng tránh được sai sót, nhưng Tôi thường vẫn còn một phần để chuỗi sniffing byte (nhưng chỉ sau khi đã đọc Spolsky on encodings) để được hoàn toàn chắc chắn bạn đang nhận được những gì bạn dự định cho các trường hợp sử dụng quan trọng. Nhắc tôi về Crockford trên "insecure regular expressions" một chút. Thông thường chúng tôi viết regexps cho phép những gì chúng tôi muốn (nếu chúng tôi may mắn), nhưng vô tình cho phép nhiều hơn (ví dụ, là $10 thực sự là một chuỗi giá trị "bắt giữ" hợp lệ trong regexp mới của tôi, ở trên?) Bởi vì chúng tôi không chu đáo đủ. Cả hai phương pháp đều có giá trị và cả hai đều khuyến khích các loại lỗi không chủ ý khác nhau. Nó thường dễ dàng đánh giá thấp sự phức tạp.

Điều lạ lùng $ thoát (và rằng Regex.Escape không thoát khỏi các mẫu giá trị được ghi như $0 như tôi đã mong đợi trong các giá trị thay thế) đã khiến tôi phát điên trong một thời gian. Lập trình là cứng (c) 1842

+0

Thực sự xứng đáng được bầu chọn nhiều hơn. Cộng với 1842, lol. :) – ewbi

1

Dưới đây là một tùy chọn để thực hiện thay thế Regex, vì không có nhiều người dường như chú ý đến trận đấu bao gồm các vị trí trong chuỗi:

public static string ReplaceCaseInsensative(this string s, string oldValue, string newValue) { 
     var sb = new StringBuilder(s); 
     int offset = oldValue.Length - newValue.Length; 
     int matchNo = 0; 
     foreach (Match match in Regex.Matches(s, Regex.Escape(oldValue), RegexOptions.IgnoreCase)) 
     { 
      sb.Remove(match.Index - (offset * matchNo), match.Length).Insert(match.Index - (offset * matchNo), newValue); 
      matchNo++; 
     } 
     return sb.ToString(); 
    } 
+0

Bạn có thể giải thích tại sao bạn đang nhân với MatchNo không? – Aheho

+0

Nếu có sự khác biệt về độ dài giữa giá trị cũ và giá trị mới, chuỗi sẽ dài hơn hoặc ngắn hơn khi bạn thay thế giá trị. match.Index đề cập đến vị trí ban đầu trong chuỗi, chúng ta cần phải điều chỉnh cho chuyển động vị trí đó do sự thay thế của chúng ta. Một cách tiếp cận khác là thực thi Remove/Insert từ phải sang trái. – Brandon

+0

Tôi hiểu điều đó. Đó là những gì biến "bù đắp" là cho. Những gì tôi không hiểu là lý do tại sao bạn đang nhân với matchNo.Trực giác của tôi nói với tôi rằng vị trí của một trận đấu trong một chuỗi sẽ không có liên quan đến số lần xuất hiện thực tế trước đó. – Aheho

8

Lấy cảm hứng từ câu trả lời cfeduke, tôi làm hàm này sử dụng IndexOf để tìm giá trị cũ trong chuỗi và sau đó thay thế nó bằng giá trị mới. Tôi sử dụng điều này trong một kịch bản SSIS xử lý hàng triệu hàng, và phương pháp regex là cách chậm hơn so với điều này.

public static string ReplaceCaseInsensitive(this string str, string oldValue, string newValue) 
{ 
    int prevPos = 0; 
    string retval = str; 
    // find the first occurence of oldValue 
    int pos = retval.IndexOf(oldValue, StringComparison.InvariantCultureIgnoreCase); 

    while (pos > -1) 
    { 
     // remove oldValue from the string 
     retval = retval.Remove(pos, oldValue.Length); 

     // insert newValue in it's place 
     retval = retval.Insert(pos, newValue); 

     // check if oldValue is found further down 
     prevPos = pos + newValue.Length; 
     pos = retval.IndexOf(oldValue, prevPos, StringComparison.InvariantCultureIgnoreCase); 
    } 

    return retval; 
} 
+0

+1 để không sử dụng regex khi không cần thiết. Chắc chắn, bạn sử dụng một vài dòng mã, nhưng nó hiệu quả hơn nhiều so với thay thế dựa trên regex trừ khi bạn cần hàm $. – ChrisG

3

Dựa trên câu trả lời Jeff Reddy, với một số optimisations và kiểm chứng thực:

public static string Replace(string str, string oldValue, string newValue, StringComparison comparison) 
{ 
    if (oldValue == null) 
     throw new ArgumentNullException("oldValue"); 
    if (oldValue.Length == 0) 
     throw new ArgumentException("String cannot be of zero length.", "oldValue"); 

    StringBuilder sb = null; 

    int startIndex = 0; 
    int foundIndex = str.IndexOf(oldValue, comparison); 
    while (foundIndex != -1) 
    { 
     if (sb == null) 
      sb = new StringBuilder(str.Length + (newValue != null ? Math.Max(0, 5 * (newValue.Length - oldValue.Length)) : 0)); 
     sb.Append(str, startIndex, foundIndex - startIndex); 
     sb.Append(newValue); 

     startIndex = foundIndex + oldValue.Length; 
     foundIndex = str.IndexOf(oldValue, startIndex, comparison); 
    } 

    if (startIndex == 0) 
     return str; 
    sb.Append(str, startIndex, str.Length - startIndex); 
    return sb.ToString(); 
} 
5

Mở rộng về câu trả lời phổ biến C. Dragon 76 's bằng cách làm cho mã của mình vào một phần mở rộng mà làm quá tải các phương pháp mặc định Replace.

public static class StringExtensions 
{ 
    public static string Replace(this string str, string oldValue, string newValue, StringComparison comparison) 
    { 
     StringBuilder sb = new StringBuilder(); 

     int previousIndex = 0; 
     int index = str.IndexOf(oldValue, comparison); 
     while (index != -1) 
     { 
      sb.Append(str.Substring(previousIndex, index - previousIndex)); 
      sb.Append(newValue); 
      index += oldValue.Length; 

      previousIndex = index; 
      index = str.IndexOf(oldValue, index, comparison); 
     } 
     sb.Append(str.Substring(previousIndex)); 
     return sb.ToString(); 
    } 
} 
0

(Vì mọi người đang chụp ảnh này). Dưới đây là phiên bản của tôi (với kiểm tra null, và đầu vào chính xác và thay thế thoát) ** Lấy cảm hứng từ khắp nơi trên Internet và các phiên bản khác:

using System; 
using System.Text.RegularExpressions; 

public static class MyExtensions { 
    public static string ReplaceIgnoreCase(this string search, string find, string replace) { 
     return Regex.Replace(search ?? "", Regex.Escape(find ?? ""), (replace ?? "").Replace("$", "$$"), RegexOptions.IgnoreCase);   
    } 
} 

Cách sử dụng:

var result = "This is a test".ReplaceIgnoreCase("IS", "was"); 
Các vấn đề liên quan