3

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

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”,则从(doing、does 等)中删除“do”。有什么更好和最快处理的建议吗?提前致谢。

4

6 回答 6

4

我可以建议一个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, "");
}

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

于 2012-05-04T11:41:05.487 回答
3

这有几个方面

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

预期结果
在示例中,您希望输出是什么?

"Did you this asking"

或者

"Did you  this   asking"

您已经在“try”和“before”的末尾添加了空格,但没有在“yourself”的末尾添加空格。为什么?错字?

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

使用 partials 很麻烦。
单词在不同的时态中变化。从“doing”词中删除“do”的例子,但是“take”和“take”呢?停用词的顺序很重要,因为您正在更改输入。有可能(我不知道有多大可能但有可能)在更改之前不在输入中的单词在更改之后“出现”在输入中。你想每次都回去重新检查吗?

你真的需要删除部分吗?

优化
当前方法将通过输入字符串 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() 方法(修改为使用字符串生成器并且不区分大小写)看起来是一个很好的首选解决方案。在尝试更复杂的事情之前,我会在可能的性能条件下对其进行基准测试。

嗯,
艾伦。

于 2012-05-04T12:56:15.210 回答
3

干得好:

var words_to_remove = new HashSet<string> { "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了非常快速的查找,所以 450 个元素words_to_remove应该没有问题。此外,我们只遍历输入字符串一次(而不是像您的示例中那样每个单词删除一次)。

但是,如果输入字符串很长,有一些方法可以提高内存效率(如果不是更快的话),方法是不要一次将拆分结果保存在内存中。

不仅要删除“do”,还要删除“doing”、“does”等……您必须在words_to_remove. 如果您想以一般方式删除前缀,则可以(相对)有效地使用一单词来删除(或者输入字符串的后缀),但是当“do”不是应该删除的东西的前缀,例如“did”?或者当它是不应该被删除的东西的前缀时,比如“狗”?

顺便说一句,要删除任何大小写的单词,只需将适当的不区分大小写的比较器传递给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在内部使用哈希表),但是很好而且整洁;)

于 2012-05-04T20:06:17.853 回答
1

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

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

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

结果将是,"Did you this before asking"

于 2012-05-04T11:42:35.160 回答
0

缩短您的代码,并使用 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());
于 2012-05-04T11:43:44.663 回答
0
String.Join(" ",input.
          Split(' ').Where(w=>stop.Where(sW=>sW==w).
                   FirstOrDefault()==null).ToArray());
于 2012-05-04T11:47:36.623 回答