169 lines
9.3 KiB
C#
169 lines
9.3 KiB
C#
using FileSearch.Logic.Model.EncodingDetection;
|
||
using FileSearch.Logic.Model.Engine;
|
||
using System.Text;
|
||
|
||
namespace FileSearch.Logic.Model.CriterionSchemas
|
||
{
|
||
internal class ContentCriterion : CriterionBase, ICriterion
|
||
{
|
||
private const int BufferSize = 32 * 1024; // 32KB
|
||
|
||
private readonly string _text;
|
||
private readonly char[][] _textInChars;
|
||
private readonly bool _ignoreCase;
|
||
private readonly bool _matchFullWords;
|
||
private readonly IEncodingFactory _encodingFactory;
|
||
|
||
public ContentCriterion(string text, bool ignoreCase, bool matchFullWords, IEncodingFactory encodingFactory)
|
||
{
|
||
if (text == null) throw new ArgumentNullException("text");
|
||
if (encodingFactory == null) throw new ArgumentNullException("encodingFactory");
|
||
|
||
_text = text;
|
||
_ignoreCase = ignoreCase;
|
||
_matchFullWords = matchFullWords;
|
||
_encodingFactory = encodingFactory;
|
||
|
||
_textInChars = StringToCharArrays(text, ignoreCase);
|
||
}
|
||
|
||
public string Name { get { return "File content"; } }
|
||
|
||
public CriterionWeight Weight { get { return CriterionWeight.Heavy; } }
|
||
|
||
public bool DirectorySupport { get { return false; } }
|
||
|
||
public bool FileSupport { get { return true; } }
|
||
|
||
public bool IsMatch(FileSystemInfo fileSystemInfo, ICriterionContext context)
|
||
{
|
||
var fileInfo = (FileInfo)fileSystemInfo;
|
||
|
||
var buffer = new byte[BufferSize];
|
||
var textLength = _text.Length;
|
||
|
||
Encoding[] encodings = new Encoding[1];
|
||
|
||
// Проверить несколько кодировок
|
||
for (int encodingIndex = 0; encodingIndex < encodings.Length; encodingIndex++)
|
||
{
|
||
// Кодирование текущего цикла. Первая попытка будет NULL.
|
||
Encoding encoding = encodings[encodingIndex];
|
||
bool characterShouldBeNonWord = false;
|
||
char characterBefore = '\0';
|
||
|
||
using (var stream = fileInfo.OpenRead())
|
||
{
|
||
int length;
|
||
int foundMatchingSymbols = 0;
|
||
while ((length = stream.Read(buffer, 0, BufferSize)) > 0)
|
||
{
|
||
// Если кодировка еще не определена, определите ее сейчас.
|
||
if (encoding == null)
|
||
{
|
||
encodings = _encodingFactory.DetectEncoding(buffer);
|
||
encoding = encodings[encodingIndex];
|
||
}
|
||
|
||
var currentString = encoding.GetString(buffer, 0, length);
|
||
var currentStringLength = currentString.Length; // Кэш
|
||
|
||
bool startAtBegin = foundMatchingSymbols > 0;
|
||
var charIndex = 0;
|
||
|
||
// Первый символ должен быть символом, отличным от слова, если предыдущие прочитанные байты заканчиваются соответствующей строкой.
|
||
if (_matchFullWords && characterShouldBeNonWord && currentStringLength > 0)
|
||
{
|
||
characterShouldBeNonWord = false;
|
||
if (!CharIsWordChar(currentString[0]))
|
||
return true;
|
||
}
|
||
|
||
// Проверьте, находится ли первый или следующий совпадающий символ в текущей строке.
|
||
if ((charIndex = currentString.IndexOfAny(_textInChars[foundMatchingSymbols], charIndex)) >= 0)
|
||
{
|
||
do
|
||
{
|
||
// Назначьте персонажа заранее.
|
||
if (charIndex > 0 && foundMatchingSymbols == 0 && _matchFullWords)
|
||
characterBefore = currentString[charIndex - 1];
|
||
|
||
// Не первый символ _text, поэтому проверьте, находится ли второй символ в первой позиции.
|
||
if (startAtBegin)
|
||
{
|
||
startAtBegin = false;
|
||
if (charIndex > 0) // Буква должна быть на первой позиции! Если нет, начните заново с первого символа в _text.
|
||
{
|
||
foundMatchingSymbols = 0;
|
||
continue;
|
||
}
|
||
}
|
||
|
||
// Скопируйте переменную, чтобы она не была изменена в приведенном ниже цикле.
|
||
var current = charIndex;
|
||
// Постарайтесь сопоставить как можно больше символов.
|
||
while (++foundMatchingSymbols < textLength && currentStringLength > ++current && (currentString[current] == _text[foundMatchingSymbols] || _ignoreCase && _textInChars[foundMatchingSymbols].Any(c => c == currentString[current]))) ;
|
||
// Нашел!
|
||
if (foundMatchingSymbols == textLength && (!_matchFullWords || !CharIsWordChar(characterBefore)))
|
||
{
|
||
if (_matchFullWords)
|
||
{
|
||
// Попытайтесь определить следующее чтение, заканчивается ли строка символом, отличным от слова.
|
||
if (current >= currentStringLength - 1)
|
||
characterShouldBeNonWord = true;
|
||
// Проверьте, не является ли следующая буква словом char. Если нет, верните true. В противном случае убедитесь, что индекс равен +1, чтобы он был сброшен в следующем операторе IF.
|
||
else if (!CharIsWordChar(currentString[++current]))
|
||
return true;
|
||
}
|
||
else
|
||
// Возвращает true, если не проверяется слово вместо части строки.
|
||
return true;
|
||
}
|
||
// Сбросить счетчик совпадающих символов, если конец текущей строки не достигнут. Если да, продолжайте тестирование при следующем чтении.
|
||
if (currentStringLength != current)
|
||
foundMatchingSymbols = 0;
|
||
// Проверьте, есть ли следующий соответствующий символ в текущей строке.
|
||
} while ((charIndex = currentString.IndexOfAny(_textInChars[foundMatchingSymbols], ++charIndex)) >= 0);
|
||
|
||
// Назначьте последний символ, чтобы в следующем раунде он знал предыдущий символ.
|
||
if (foundMatchingSymbols == 0 && _matchFullWords)
|
||
characterBefore = currentString[currentStringLength - 1];
|
||
}
|
||
else
|
||
{
|
||
foundMatchingSymbols = 0;
|
||
// Переназначьте предыдущий символ последнему символу в строке.
|
||
if (_matchFullWords) characterBefore = currentString[currentStringLength - 1];
|
||
}
|
||
}
|
||
if (_matchFullWords && characterShouldBeNonWord)
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
private static bool CharIsWordChar(char c)
|
||
{
|
||
return char.IsLetterOrDigit(c) || c == '_';
|
||
}
|
||
|
||
private static char[][] StringToCharArrays(string input, bool ignoreCase)
|
||
{
|
||
var list = new List<char[]>();
|
||
foreach (var c in input)
|
||
{
|
||
if (!ignoreCase || !char.IsLetter(c))
|
||
list.Add(new[] { c });
|
||
else
|
||
{
|
||
var u = char.ToUpperInvariant(c);
|
||
var l = char.ToLowerInvariant(c);
|
||
list.Add(u != l ? new[] { u, l } : new[] { c });
|
||
}
|
||
}
|
||
return list.ToArray();
|
||
}
|
||
}
|
||
}
|