从文本C#中删除停用词

我想从输入字符串中删除一组停用词,我有以下过程

string[] arrToCheck = new string[] { "try ", "yourself", "before " }; string input = "Did you try this yourself before asking"; foreach (string word in arrToCheck ) { input = input.Replace(word, ""); } 

这是执行此任务的最佳方式,特别是当我有(450)停用单词且输入字符串很长时? 我更喜欢使用替换方法,因为我想在它们以不同的形态出现时删除停用词。 例如,如果停用词是“do”,则从(do,do等)中删除“do”。 有没有更好,最快的处理建议? 提前致谢。

我可以建议一个StringBuilder吗?

http://msdn.microsoft.com/en-us/library/system.text.stringbuilder.aspx

 string[] arrToCheck = new string[] { "try ", "yourself", "before " }; StringBuilder input = new StringBuilder("Did you try this yourself before asking"); foreach (string word in arrToCheck ) { input.Replace(word, ""); } 

因为它在它自己的数据结构中完成所有处理,并且没有分配数百个新字符串,我相信你会发现它的内存效率要高得多。

这有几个方面

过早优化
给出的方法有效且易于理解/维护。 是否会导致性能问题? 如果没有,那就不用担心了。 如果它曾经导致问题,那么看看它。

预期成绩
在这个例子中,你想要输出的是什么?

 "Did you this asking" 

要么

 "Did you this asking" 

你已经在“尝试”和“之前”的末尾添加了空格,而不是“你自己”。 为什么? 错字?

string.Replace()区分大小写。 如果您关心套管,则需要修改代码。

使用partials是混乱的。
单词在不同时态发生变化。 “做”的例子被从“做”字中删除,但是如何“接受”和“接受”? 停止词的顺序很重要,因为您正在更改输入。 有可能(我不知道有多大可能但是可能)在更改之后输入中没有出现的单词“出现”。 你想每次回去重新检查吗?

你真的需要删除部分?

优化
当前方法将在输入字符串中工作n次,其中n是要编辑的字数,每次发生替换时都会创建一个新字符串。 这慢。

使用StringBuilder (上面的akatakritos)会加快这个速度,所以我先尝试一下。 重新测试,看看这是否足够快。

可以使用Linq

编辑
只是通过”来展示。 您还需要允许标点符号并决定它们应该发生什么。
结束编辑

 [TestMethod] public void RedactTextLinqNoPartials() { var arrToCheck = new string[] { "try", "yourself", "before" }; var input = "Did you try this yourself before asking"; var output = string.Join(" ",input.Split(' ').Where(wrd => !arrToCheck.Contains(wrd))); Assert.AreEqual("Did you this asking", output); } 

将删除所有整个单词(和空格。不可能看到删除单词的位置)但没有一些基准测试我不会说它更快。

使用linq处理部分内容会变得混乱但如果我们只需要一次通过就可以工作(不检查’发现的’单词’)

 [TestMethod] public void RedactTextLinqPartials() { var arrToCheck = new string[] { "try", "yourself", "before", "ask" }; var input = "Did you try this yourself before asking"; var output = string.Join(" ", input.Split(' ').Select(wrd => { var found = arrToCheck.FirstOrDefault(chk => wrd.IndexOf(chk) != -1); return found != null ? wrd.Replace(found,"") : wrd; }).Where(wrd => wrd != "")); Assert.AreEqual("Did you this ing", output); } 

只是从看这个我会说它比string.Replace()慢,但没有一些数字,没有办法告诉。 这肯定更复杂。

底线
String.Replace()方法(修改为使用字符串构建器并且不区分大小写)看起来像是一个很好的第一个解决方案。 在尝试任何更复杂的事情之前,我会在可能的性能条件下对其进

心连心,
艾伦。

对于从句子中删除字符串列表并将结果重新聚合在一起的简单方法,您可以执行以下操作:

 var input = "Did you try this yourself before asking"; var arrToCheck = new [] { "try ", "yourself", "before " }; var result = input.Split(arrToCheck, arrToCheck.Count(), StringSplitOptions.None) .Aggregate((first, second) => first + second); 

这将使用单词分隔符将原始字符串分开,并使用split数组中的结果集创建一个最终字符串。

结果将是"Did you this before asking"

缩短你的代码,并使用LINQ

 string[] arrToCheck = new string[] { "try ", "yourself", "before " }; var test = new StringBuilder("Did you try this yourself before asking"); arrToCheck.ForEach(x=> test = test.Replace(x, "")); Console.Writeln(test.ToString()); 
 String.Join(" ",input. Split(' ').Where(w=>stop.Where(sW=>sW==w). FirstOrDefault()==null).ToArray()); 

干得好:

 var words_to_remove = new HashSet { "try", "yourself", "before" }; string input = "Did you try this yourself before asking"; string output = string.Join( " ", input .Split(new[] { ' ', '\t', '\n', '\r' /* etc... */ }) .Where(word => !words_to_remove.Contains(word)) ); Console.WriteLine(output); 

这打印:

 Did you this asking 

HashSet提供极快的查找,因此words_to_remove 450个元素应该没有任何问题。 此外,我们只遍历输入字符串一次 (而不是每个单词移动一次,如您的示例中所示)。

但是,如果输入字符串非常长,则可以通过不将拆分结果一次性保存在内存中来提高内存效率(如果不是更快)。

要删除不只是“做”,而是“做”,“做”等……你必须在words_to_remove包含所有这些变体。 如果你想以一般的方式删除前缀,这可以(相对)有效地使用一些单词来删除(或者输入字符串的后缀树 ),但是当“do” 不是 a时该怎么办应该删除的东西的前缀,例如“did”? 或者当它是不应删除的东西的前缀时,例如“dog”?

顺便说一句,要删除单词,无论它们的情况如何,只需将适当的不区分大小写的比较器传递给HashSet构造函数,例如StringComparer.CurrentCultureIgnoreCase 。

—编辑—

这是另一种选择:

 var words_to_remove = new[] { " ", "try", "yourself", "before" }; // Note the space! string input = "Did you try this yourself before asking"; string output = string.Join( " ", input.Split(words_to_remove, StringSplitOptions.RemoveEmptyEntries) ); 

我猜它应该更慢(除非string.Split在内部使用哈希表),但是很好又整洁;)