2010-10-05 24 views
5

Có một danh sách các từ bị cấm (hoặc chuỗi tổng quát hơn) và một danh sách khác với giả sử là thư của người dùng. Tôi muốn loại bỏ tất cả các từ bị cấm từ tất cả các thư.Cách cắt các từ được chỉ định từ chuỗi

dụ Trivial:

foreach(string word in wordsList) 
{ 
    foreach(string mail in mailList) 
    { 
     mail.Replace(word,String.Empty); 
    } 
} 

Làm thế nào tôi có thể cải thiện thuật toán này?


Cảm ơn lời khuyên. Tôi đã bỏ phiếu cho vài câu trả lời nhưng tôi không đánh dấu bất kỳ câu trả lời nào vì nó giống như thảo luận hơn là giải pháp. Một số người đã bỏ lỡ những từ bị cấm với những từ xấu. Trong trường hợp của tôi, tôi không phải bận tâm về việc nhận ra 'sh1t' hoặc một cái gì đó như thế.

+10

Bạn có gặp sự cố về hiệu suất với điều này không? Đừng tối ưu hóa cho đến khi cần thiết. – Oded

+1

Tôi không gặp vấn đề về hiệu suất. Tôi chỉ muốn học và nâng cao kỹ năng của mình :-) – zgorawski

Trả lời

2

Bạn có thể sử dụng RegEx để làm cho mọi việc một chút bụi:

var bannedWords = @"\b(this|is|the|list|of|banned|words)\b"; 

foreach(mail in mailList) 
    var clean = Regex.Replace(mail, bannedWords, "", RegexOptions.IgnoreCase); 

Thậm chí đó, tuy nhiên, còn xa mới hoàn hảo từ mọi người sẽ luôn tìm ra một con đường xung quanh bất kỳ loại bộ lọc.

+0

Đây không phải là loại bỏ các từ bị cấm, nó loại bỏ các chất bị cấm.Ví dụ, điều này sẽ thay đổi từ "thường" trong một chuỗi thành "mười". –

+0

@Michael - Rõ ràng là RegEx-Fu của tôi không phải là để ngửi. Tôi đã thêm vào những gì tôi nghĩ là đúng cách để giới hạn ranh giới từ. Bất kỳ sửa chữa nào? –

+0

Điều đó có vẻ tốt hơn, cảm ơn. Mặc dù tôi sẽ đề cập đến một lần nữa (như dưới đây) rằng nó có thể không phải là lý tưởng để thực hiện một Regex như thế này nếu danh sách của bạn là nhiều hơn một vài chục từ. –

5

Phương pháp tiếp cận đơn giản để lọc thô tục sẽ không hoạt động - các phương pháp phức tạp không hoạt động, phần lớn là.

Điều gì xảy ra khi bạn nhận được một tác phẩm như 'mật khẩu' và bạn muốn lọc 'ass'? Điều gì xảy ra khi một số người thông minh viết 'a $$' thay thế - ý định vẫn rõ ràng, đúng không?

Xem How do you implement a good profanity filter? để thảo luận sâu rộng.

+0

"Điều gì sẽ xảy ra khi bạn nhận được một tác phẩm như 'mật khẩu' và bạn muốn lọc 'ass'?" - Sau đó thuật toán của bạn hút. –

+1

"Điều gì xảy ra khi một số người thông minh viết 'a $$' thay thế - ý định vẫn rõ ràng, đúng không?" - Rất thường xuyên giảm một vấn đề có giá trị, một sửa chữa 100% cho một vấn đề không phải lúc nào cũng cần thiết. –

+0

@Brian - đã đồng ý, tôi đang đọc giữa các dòng ở đây. Nếu OP chỉ muốn xây dựng mã 'nỗ lực tốt nhất', thì điều chỉnh để thay thế chuỗi là tốt. Nếu anh ta/cô ấy đã đăng ký để xây dựng một bộ lọc thô tục đáng tin cậy, thì phạm vi nỗ lực cần phải rõ ràng, hoặc anh ta/cô ấy có thể gặp rắc rối khi phải mất nhiều thời gian hơn dự kiến. –

2

Bạn sẽ nhận được hiệu suất tốt nhất bằng cách vẽ lên một finite state machine (FSM) (hoặc tạo một) và sau đó phân tích cú pháp đầu vào 1 ký tự cùng một lúc và đi qua các trạng thái. Bạn có thể thực hiện điều này khá dễ dàng với chức năng lấy thẻ đầu vào tiếp theo và trạng thái hiện tại của bạn và trả về trạng thái tiếp theo, bạn cũng tạo đầu ra khi bạn đi qua các ký tự của thư. Bạn vẽ FSM trên một tờ giấy.

Hoặc bạn có thể xem xét Windows Workflow Foundation: State Machine Workflows.

Bằng cách đó, bạn chỉ cần đi bộ từng thư một lần.

+0

Trừ khi tôi hiểu sai ý kiến ​​của bạn, tôi cảm thấy giống như sử dụng Windows Stateflow State Machine về vấn đề này để phân tích cú pháp một ký tự chuỗi ký tự là một chút quá mức cần thiết. –

+0

Điều đó phụ thuộc vào phần mềm là gì. Nếu người đó đang cố gắng xây dựng một phần mềm lọc thô tục, thì tôi sẽ không nghĩ vậy. –

0

Bạn có thể xem xét sử dụng Regex thay vì các kết hợp chuỗi đơn giản, để tránh thay thế một phần nội dung trong các từ. Regex sẽ cho phép bạn đảm bảo rằng bạn chỉ nhận được các từ đầy đủ phù hợp. Bạn có thể sử dụng mẫu như sau:

"\bBADWORD\b" 

Ngoài ra, bạn có thể muốn lặp qua danh sách thư ở bên ngoài và danh sách từ trên vòng lặp bên trong.

1

Xây dựng biểu thức chính quy từ các từ (word1|word2|word3|...) và sử dụng cụm từ này thay vì vòng ngoài có thể nhanh hơn, từ đó, mọi email chỉ cần được phân tích cú pháp một lần. Ngoài ra, việc sử dụng cụm từ thông dụng sẽ cho phép bạn chỉ xóa "từ hoàn chỉnh" bằng cách sử dụng các điểm đánh dấu ranh giới từ (\b(word1|word2|word3|...)\b).

Nói chung, tôi không nghĩ rằng bạn sẽ tìm thấy một giải pháp đó là thứ tự độ lớn nhanh hơn so với hiện tại của bạn một: Bạn sẽ phải lặp qua tất cả các thư và bạn sẽ phải tìm kiếm tất cả các từ , không có cách nào dễ dàng.

1

Một thuật toán nói chung sẽ được:

  1. Tạo một danh sách các thẻ dựa trên chuỗi đầu vào
  2. Hãy so sánh mỗi thẻ chống lại một danh sách các từ cấm
  3. (ví dụ bằng cách xử lý khoảng trắng phân cách như token.)
  4. Thay thế mã thông báo phù hợp

Biểu thức chính quy thuận tiện để xác định mã thông báo và một HashSet sẽ tra cứu nhanh danh sách các từ bị cấm. Có một phương thức quá tải Replace trên lớp Regex có chức năng, nơi bạn có thể kiểm soát hành vi thay thế dựa trên tra cứu của mình.

HashSet<string> BannedWords = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase) 
{ 
    "bad", 
}; 

string Input = "this is some bad text."; 

string Output = Regex.Replace(Input, @"\b\w+\b", (Match m) => BannedWords.Contains(m.Value) ? new string('x', m.Value.Length) : m.Value); 
+0

Điều này không sử dụng sức mạnh của Regex. Nó chỉ tóm tắt vòng lặp thay thế. Xem [Câu trả lời của Justin] (http://stackoverflow.com/questions/3864678/how-to-cut-specified-words-from-string/3864743#3864743) cho ý tôi. –

+0

@Ahmad Mageed: Tôi đang sử dụng cụm từ thông dụng đơn giản (và nhanh) để tạo ra một luồng mã thông báo từ một chuỗi - tôi cần thêm sức mạnh nào? Tôi cũng không nghĩ rằng đó là lý tưởng (hoặc thực hiện) để có hàng trăm từ bị cấm và xây dựng một biểu thức chính quy lớn như trong giải pháp của Justin. –

0

Nó sẽ không dễ dàng hơn (và hiệu quả hơn) để đơn giản hóa lại chúng bằng cách thay đổi tất cả các ký tự thành * hoặc gì đó? Bằng cách đó không có chuỗi lớn cần phải được thay đổi kích thước hoặc di chuyển xung quanh, và những người ủng hộ được thực hiện ý thức hơn những gì đã xảy ra, thay vì nhận được những câu vô nghĩa với những từ bị thiếu.

+0

Tại sao điều này lại hiệu quả hơn? – Heinzi

+0

@Heinzi - Được chỉnh sửa để bao gồm thông tin đó. Về cơ bản, Thay thế sẽ phải di chuyển dữ liệu sau chuỗi được thay thế xung quanh, trừ khi bạn thay thế nó bằng số ký tự chính xác. –

+0

'Replace' sẽ tạo một thể hiện String hoàn toàn mới, vì các chuỗi là không thay đổi. Tôi đồng ý với điểm khả năng sử dụng của bạn, mặc dù! – Heinzi

1

Thay thế bằng * gây khó chịu, nhưng ít gây phiền nhiễu hơn điều gì đó loại bỏ bối cảnh ý định của bạn bằng cách xóa từ và để lại một câu không đúng định dạng. Khi thảo luận về trận Hastings, tôi sẽ bị kích thích nếu tôi thấy William được trao danh hiệu "Grand ******* of Normandy"", nhưng ít nhất tôi biết rằng tôi đang chơi trong sân chơi nhỏ, trong khi ông có danh hiệu "Grand of Normandy" trông giống như một sai lầm, hoặc (tệ hơn) Tôi có thể nghĩ đó thực sự là tiêu đề của anh ấy.

Đừng cố gắng thay thế các từ với những từ vô hại hơn trừ khi nó buồn cười. những người bối rối vì thời gian medireview và mediareview đã được thảo luận khi eval (không thô tục, nhưng được sử dụng trong một số cuộc tấn công XSS mà yahoo đã bị tấn công) được thay thế bằng đánh giá trong thời trung cổ và thời trung cổ (cách ly, medireview là chính tả của Mỹ về mediareview!)

+0

Điều này khá giống với câu trả lời của tôi và được gửi gần như cùng một lúc. Bất cứ khi nào điều đó xảy ra, chính sách chung của tôi là người gửi rõ ràng là một thiên tài và xứng đáng là +1. :-) –

0

Vâng, bạn chắc chắn don 'muốn làm cho sai lầm clbuttic của string.Replace ngây thơ() để làm điều đó. Các giải pháp regex có thể làm việc, mặc dù bạn muốn hoặc là lặp lại hoặc sử dụng máy phát điện ống (và tôi không biết nếu có bao nhiêu sẽ làm chậm hoạt động của bạn xuống, đặc biệt là cho một danh sách lớn các từ bị cấm). Bạn có thể luôn luôn chỉ ... không làm điều đó, vì nó hoàn toàn vô ích bất kể điều gì - có nhiều cách để làm cho những từ bạn định nói khá rõ ràng ngay cả khi không sử dụng các chữ cái chính xác.

Điều đó và thật lố bịch khi có danh sách các từ mà "mọi người thấy khó chịu" ngay từ đầu.Có ai đó sẽ được hài lòng bởi khá nhiều bất kỳ từ

/kiểm duyệt là nhảm nhí rant

1

Trong một số tình huống có thể cải thiện nó: Just for fun:

u có thể sử dụng SortedList, nếu gửi thư ur danh sách là danh sách gửi thư (vì u có dấu phân cách như ";") u có thể làm như dưới đây:

lần đầu tiên tính toán thuật toán thời gian chạy ur: Từ: n mục. (mỗi mục có độ dài O (1)). danh sách gửi thư: mục K. mỗi mục trong danh sách gửi thư có độ dài trung bình là Z. mỗi mục con trong mục danh sách gửi thư có chiều dài trung bình của Y để số lượng trung bình của các mục con trong các mục danh sách gửi thư là m = Z/Y.

thuật toán ur mất O (n * K * Z). // cách tốt nhất với thuật toán knut

1.xác định nếu bạn sắp xếp danh sách từ trong O (n log n).

2.1- sử dụng mailingListItem.Split (";". ToCharArray()) cho từng mục danh sách gửi thư: O (Z). 2.2- sắp xếp các mục trong danh sách gửi thư: O (m * log m) tổng sắp xếp mất O (K * Z) trong trường hợp đáng giá (m logm < < Z).

3 sử dụng kết hợp thuật toán để hợp nhất các hạng mục từ xấu và mailing list cụ thể: O ((m + n) * k)

tổng thời gian là O ((m + n) * K + m * Z + n^2) đối với m < < n, tổng thời gian chạy thuật toán là O (n^2 + Z * K) trong trường hợp có giá trị, nhỏ hơn O (n * K * Z) nếu n < K * Z (tôi nghĩ vậy).

Vì vậy, nếu hiệu suất rất rất quan trọng, bạn có thể thực hiện việc này.

0

Tôi giả định rằng bạn muốn phát hiện các từ hoàn chỉnh (được phân tách bằng các ký tự không phải chữ cái) và bỏ qua các từ bằng chuỗi con từ bộ lọc (như ví dụ từ p [ass]). Trong trường hợp đó, bạn nên xây dựng cho mình một HashSet các từ bộ lọc, quét văn bản cho các từ và mỗi từ kiểm tra sự tồn tại của nó trong HashSet. Nếu đó là một từ bộ lọc thì hãy xây dựng đối tượng StringBuilder kết quả mà không có nó (hoặc với một số dấu hoa thị).

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