首先,我希望您并没有真正“标记”所写的文字。你知道,仅仅因为有人没有在坏词前加一个空格,它不会让这个词变得不那么糟糕:-) 示例,badword,
我会说我会在这里使用 Regex :-) 我不确定 Regex 或人造解析器是否会更快,但至少 Regex 会是一个很好的起点。正如其他人所写,您首先将文本拆分为单词,然后检查HashSet<string>。
我正在添加基于ArraySegment<char> 的代码的第二个版本。我稍后再谈。
class Program
{
class ArraySegmentComparer : IEqualityComparer<ArraySegment<char>>
{
public bool Equals(ArraySegment<char> x, ArraySegment<char> y)
{
if (x.Count != y.Count)
{
return false;
}
int end = x.Offset + x.Count;
for (int i = x.Offset, j = y.Offset; i < end; i++, j++)
{
if (!x.Array[i].ToString().Equals(y.Array[j].ToString(), StringComparison.InvariantCultureIgnoreCase))
{
return false;
}
}
return true;
}
public override int GetHashCode(ArraySegment<char> obj)
{
unchecked
{
int hash = 17;
int end = obj.Offset + obj.Count;
int i;
for (i = obj.Offset; i < end; i++)
{
hash *= 23;
hash += Char.ToUpperInvariant(obj.Array[i]);
}
return hash;
}
}
}
static void Main()
{
var rx = new Regex(@"\b\w+\b", RegexOptions.Compiled);
var sampleText = @"For my custom made chat screen i am using the code below for checking censored words. But i wonder can this code performance improved. Thank you.
if (srMessageTemp.IndexOf("" censored1 "") != -1)
return;
if (srMessageTemp.IndexOf("" censored2 "") != -1)
return;
if (srMessageTemp.IndexOf("" censored3 "") != -1)
return;
C# 4.0 . actually list is a lot more long but i don't put here as it goes away.
And now some accented letters àèéìòù and now some letters with unicode combinable diacritics àèéìòù";
//sampleText += sampleText;
//sampleText += sampleText;
//sampleText += sampleText;
//sampleText += sampleText;
//sampleText += sampleText;
//sampleText += sampleText;
//sampleText += sampleText;
HashSet<string> prohibitedWords = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase) { "For", "custom", "combinable", "away" };
Stopwatch sw1 = Stopwatch.StartNew();
var words = rx.Matches(sampleText);
foreach (Match word in words)
{
string str = word.Value;
if (prohibitedWords.Contains(str))
{
Console.Write(str);
Console.Write(" ");
}
else
{
//Console.WriteLine(word);
}
}
sw1.Stop();
Console.WriteLine();
Console.WriteLine();
HashSet<ArraySegment<char>> prohibitedWords2 = new HashSet<ArraySegment<char>>(
prohibitedWords.Select(p => new ArraySegment<char>(p.ToCharArray())),
new ArraySegmentComparer());
var sampleText2 = sampleText.ToCharArray();
Stopwatch sw2 = Stopwatch.StartNew();
int startWord = -1;
for (int i = 0; i < sampleText2.Length; i++)
{
if (Char.IsLetter(sampleText2[i]) || Char.IsDigit(sampleText2[i]))
{
if (startWord == -1)
{
startWord = i;
}
}
else
{
if (startWord != -1)
{
int length = i - startWord;
if (length != 0)
{
var wordSegment = new ArraySegment<char>(sampleText2, startWord, length);
if (prohibitedWords2.Contains(wordSegment))
{
Console.Write(sampleText2, startWord, length);
Console.Write(" ");
}
else
{
//Console.WriteLine(sampleText2, startWord, length);
}
}
startWord = -1;
}
}
}
if (startWord != -1)
{
int length = sampleText2.Length - startWord;
if (length != 0)
{
var wordSegment = new ArraySegment<char>(sampleText2, startWord, length);
if (prohibitedWords2.Contains(wordSegment))
{
Console.Write(sampleText2, startWord, length);
Console.Write(" ");
}
else
{
//Console.WriteLine(sampleText2, startWord, length);
}
}
}
sw2.Stop();
Console.WriteLine();
Console.WriteLine();
Console.WriteLine(sw1.ElapsedTicks);
Console.WriteLine(sw2.ElapsedTicks);
}
}
我会注意到,您可以更快地“在”原始字符串中进行解析。这意味着什么:如果您将“文档”细分为单词,并且每个单词都放在string 中,那么显然您正在创建n string,为文档的每个单词创建一个。但是如果你跳过这一步,直接对文档进行操作,只保留当前索引和当前单词的长度呢?那么它会更快!显然,您需要为HashSet<> 创建一个特殊的比较器。
但是等等! C# 有类似的东西......它被称为ArraySegment。因此,您的文档将是 char[] 而不是 string,并且每个单词都是 ArraySegment<char>。显然这要复杂得多!您不能简单地使用Regexes,您必须“手动”构建解析器(但我认为转换\b\w+\b 表达式会很容易)。并且为HashSet<char> 创建一个比较器会有点复杂(提示:您将使用HashSet<ArraySegment<char>> 并且要审查的单词将是ArraySegments“指向”一个单词的char[],大小等于char[].Length,比如var word = new ArraySegment<char>("tobecensored".ToCharArray());)
经过一些简单的基准测试后,我可以看到使用ArraySegment<string> 的程序的未优化版本与Regex 版本一样快对于较短的文本。这可能是因为如果一个词的长度为 4-6 个字符,那么复制它比复制一个 ArraySegment<char>(ArraySegment<char> 是 12 个字节,一个 6 个字符的词是 12 个字节)要“慢”得多。在这两者之上,我们必须增加一点开销......但最终数字是可比的)。但是对于较长的文本(尝试取消注释 //sampleText += sampleText;),它在 Release -> Start without Debugging (CTRL-F5) 中变得更快(10%)
我会注意到逐个字符比较字符串是错误的。您应该始终使用string 类(或操作系统)提供给您的方法。他们知道如何比你更好地处理“奇怪”的情况(在 Unicode 中没有任何“正常”的情况 :-))