2010-08-17 36 views
19

Tôi đang viết nội dung của một tệp văn bản vào một StringBuilder và sau đó tôi muốn thực hiện một số hành động tìm/thay thế trên văn bản có trong StringBuilder bằng cách sử dụng các biểu thức chính quy.Thay thế Regex bên trong một StringBuilder

Tôi đã gặp sự cố khi hàm thay thế StringBuilder không có khả năng chấp nhận đối số cụm từ thông dụng.

Tôi có thể sử dụng Regex.Replace trên một chuỗi bình thường nhưng tôi theo ấn tượng rằng điều này là không hiệu quả do thực tế là hai bản sao của chuỗi sẽ cần phải được tạo ra trong bộ nhớ như chuỗi .net là bất biến.

Khi tôi đã cập nhật văn bản, tôi định viết nó trở lại tệp gốc.

Cách tốt nhất và hiệu quả nhất để giải quyết vấn đề của tôi là gì?

EDIT

Ngoài các câu trả lời (s) dưới đây, tôi đã tìm thấy các câu hỏi sau đó cũng làm sáng tỏ về vấn đề của tôi -

Trả lời

23

Giải pháp tốt nhất và hiệu quả nhất cho thời gian của bạn là thử phương pháp đơn giản nhất trước tiên: quên StringBuilder và chỉ sử dụng Regex.Replace. Sau đó tìm hiểu xem nó chậm đến mức nào - nó có thể rất tốt. Đừng quên thử regex trong cả hai chế độ biên soạn và không biên dịch.

Nếu điều đó không phải là đủ nhanh, hãy xem xét sử dụng StringBuilder cho bất kỳ thay thế nào bạn có thể thể hiện đơn giản, sau đó sử dụng Regex.Replace cho phần còn lại. Bạn cũng có thể muốn xem xét cố gắng kết hợp các thay thế, giảm số lượng các regex (và do đó các chuỗi trung gian) được sử dụng.

+1

Tôi ngạc nhiên vì tôi không nghĩ về điều này: thực sự chạy nó và thấy, thay vì suy đoán về tốc độ sẽ là gì. Tôi đã xóa câu trả lời đầu cơ của mình cho phù hợp. – Timwi

+1

Nếu Regex.Replace đủ nhanh, tôi có nên quan tâm đến quản lý bộ nhớ không? Tôi có đang phân tích/tối ưu hóa mọi thứ bằng cách lo lắng về bộ nhớ over-head của việc tạo ra nhiều chuỗi không? – ipr101

+0

Đây không phải là câu trả lời nhiều như gợi ý. Câu hỏi đặt ra là làm thế nào để làm cho regex làm việc với stringbuilder, và câu trả lời là chúng không tương thích trừ khi bạn viết thực hiện của riêng bạn. Tại sao đây là trường hợp tôi không biết. – Slight

1

Tôi không chắc chắn nếu điều này giúp kịch bản của bạn hay không, nhưng tôi chạy vào một số tiêu thụ bộ nhớ trần với Regex và tôi cần một phương pháp mở rộng thay thế ký tự đại diện đơn giản trên một StringBuilder để đẩy qua nó. Nếu bạn cần phức tạp Regex phù hợp và/hoặc backreferences, điều này sẽ không làm, nhưng nếu đơn giản * hoặc? thay thế ký tự đại diện (với nghĩa đen "thay thế" text) sẽ hoàn thành công việc cho bạn, thì cách giải quyết vào cuối câu hỏi của tôi ở đây nên ít nhất cung cấp cho bạn một tăng:

Has anyone implemented a Regex and/or Xml parser around StringBuilders or Streams?

0

Dưới đây là một phương pháp mở rộng bạn có thể sử dụng để thực hiện những gì bạn muốn. Phải mất một Dictionary trong đó khóa là mẫu bạn đang tìm kiếm và giá trị là thứ bạn muốn thay thế bằng. Bạn vẫn tạo bản sao của chuỗi đến nhưng bạn chỉ phải đối phó với điều này một lần thay vì tạo bản sao cho nhiều cuộc gọi đến Regex.Replace.

public static StringBuilder BulkReplace(this StringBuilder source, IDictionary<string, string> replacementMap) 
{ 
    if (source.Length == 0 || replacementMap.Count == 0) 
    { 
     return source; 
    } 
    string replaced = Regex.Replace(source.ToString(), String.Join("|", replacementMap.Keys.Select(Regex.Escape).ToArray()), m => replacementMap[m.Value], RegexOptions.IgnoreCase); 
    return source.Clear().Append(replaced); 
} 
+1

Điểm của việc sử dụng Regex với StringBuilder không chỉ đơn giản là có một phương thức thực hiện công việc, mà còn để giảm thiểu lãng phí bộ nhớ, đặc biệt tránh một số lượng lớn các chuỗi trung gian được lưu trữ trong bộ nhớ. –

+0

Nó không hoàn hảo vì bạn phải chuyển đổi StringBuilder thành một chuỗi, nhưng phương pháp này nhanh hơn khoảng 4 lần so với việc gọi Regex.Replace trên một chuỗi lặp đi lặp lại. –

+0

Nếu bản đồ thay thế chứa mẫu, bạn sẽ nhận được: "Khóa đã cho không có trong từ điển". Điều này được mong đợi vì giá trị m.Value từ bản đồ thay thế [m.Value] tìm kiếm một khóa là chuỗi actula phù hợp với mẫu đó chứ không phải chính mẫu đó. Tui bỏ lỡ điều gì vậy? Theo mẫu, tôi có nghĩa là các chuỗi mẫu regex như: "" <[^>] +> "và không phải các chuỗi chính xác như"
mmmmmm

2

Bạn có 3 lựa chọn:

  1. Làm điều này một cách hiệu quả với chuỗi như những người khác đã đề nghị ở đây.

  2. Sử dụng gọi .Matches() trên đối tượng Regex của bạn và mô phỏng cách hoạt động .Replace() (xem # 3).

  3. Thích ứng việc thực hiện Mono của Regex để xây dựng một Regex chấp nhận StringBuilder (và hãy chia sẻ nó ở đây!) Hầu như tất cả các công việc đã được thực hiện cho bạn trong Mono, nhưng nó sẽ mất thời gian để Suss ra những phần làm cho nó hoạt động vào thư viện của riêng họ. Mono Regex thúc đẩy triển khai JVM 2002 của Novell là Regex, đủ kỳ quặc.

Trong Mono:

System.Text.RegularExpressions.Regex sử dụng một RxCompiler nhanh chóng một IMachineFactory trong hình thức của một RxInterpreterFactory, mà gì ngạc nhiên làm cho IMachine s như RxInterpreter s. Bắt những người phát ra là hầu hết những gì bạn cần làm, mặc dù nếu bạn chỉ muốn tìm hiểu cách tất cả được cấu trúc để có hiệu quả, thì điều đáng chú ý là những gì bạn đang tìm kiếm nằm trong lớp cơ sở, BaseMachine.

Cụ thể, trong BaseMachine là công cụ StringBuilder. Trong phương thức LTRReplace, đầu tiên nó khởi tạo một StringBuilder với chuỗi ban đầu và mọi thứ từ đó trở đi hoàn toàn dựa trên StringBuilder. Nó thực sự rất khó chịu rằng Regex không có phương thức StringBuilder treo ra, nếu chúng ta giả định việc thực hiện Microsoft .Net nội bộ là tương tự.

Quanh trở lại gợi ý 2, bạn có thể bắt chước hành vi LTRReplace 's bằng cách gọi .Matches(), theo dõi bạn đang ở đâu trong chuỗi ban đầu, và vòng lặp:

var matches = regex.Matches(original); 
var sb = new StringBuilder(original.Length); 
int pos = 0; // position in original string 
foreach(var match in matches) 
{ 
    sb.Append(original.Substring(pos, match.Index)); // Append the portion of the original we skipped 
    pos = match.Index; 

    // Make any operations you like on the match result, like your own custom Replace, or even run another Regex 

    pos += match.Value.Length; 
} 
sb.Append(original.Substring(pos, original.Length - 1)); 

Nhưng, điều này chỉ giúp bạn tiết kiệm một số dây - các Cách tiếp cận mod-Mono là cách duy nhất thực sự làm đúng.

Các vấn đề liên quan