Reputation: 2261
I have implemented such a method:
static void Main(string[] args)
{
string s = "05 AA 06 15 14";
var pattern1 = "05 AA";
var pattern2 = "06 15";
var pattern3 = "AA ** 15";
var pattern4 = "15 14";
Console.WriteLine(CheckTextContains2(s, pattern1));
Console.WriteLine(CheckTextContains2(s, pattern2));
Console.WriteLine(CheckTextContains2(s, pattern3));
Console.WriteLine(CheckTextContains2(s, pattern4));
}
private static int CheckTextContains2(string text, string pattern)
{
if (text.Length < pattern.Length)
return -1;
for (int i = 0; i < text.Length - pattern.Length +1; i++)
{
for (int j = 0; j < pattern.Length; j++)
{
if (pattern[j] == '*')
continue;
else if (text[j + i] != pattern[j])
break;
else if (j == pattern.Length -1)
return i;
}
}
return -1;
}
The method checks if the given pattern is in the string, but ignore chars "*" and return start index.
But it seems terribly suboptimal, Can I getting it by regex?
Upvotes: 1
Views: 245
Reputation: 3446
Yes. You can use Regex.Match(s, pattern)
to check for position. The only change in pattern3 is that you have to replace *
by .
.
The code might look like this:
string s = "05 AA 06 15 14";
var pattern1 = "05 AA";
var pattern2 = "06 15";
var pattern3 = "AA .. 15";
var pattern4 = "15 14";
var posP1 = Regex.Match(s, pattern1) is { Success: true } m1 ? m1.Index : -1;
var posP2 = Regex.Match(s, pattern2) is { Success: true } m2 ? m2.Index : -1;
var posP3 = Regex.Match(s, pattern3) is { Success: true } m3 ? m3.Index : -1;
var posP4 = Regex.Match(s, pattern4) is { Success: true } m4 ? m4.Index : -1;
Console.WriteLine(posP1);
Console.WriteLine(posP2);
Console.WriteLine(posP3);
Console.WriteLine(posP4);
The output is:
0
6
3
9
Explanation:
A .
in regex is a single character wildcard. All the other characters in your pattern doesn't affect the regex and will be used as fixed text to find. You have to be a bit carefully about chars like \[]{}.()*+
. But if your pattern doesn't contain any of those regex will work like string.IndexOf()
.
The Regex.Match()
method will return a Match
instance that contains a property that has to be checked for Success
. The sample code does it using pattern matching to be able to write it in one line.
Keep in mind that you should cache the regexes with this._pattern1Finder = new Regex(pattern1, RegexOptions.Compiled)
to avoid the regex construction at every execution. The call will look like this._pattern1Finder.Match(s) is { ...
instead of using the static Methods of Regex
. This depends on how often you try to match the same patterns against various inputs.
Upvotes: 2
Reputation: 2453
If the contest is: the-shorter-the-better, I'd like to introduce:
private static int CheckTextContains(string text, string pattern)
{
if (text.Length >= pattern.Length) {
for (var offset = 0; offset < text.Length - pattern.Length; offset++) {
if (!pattern.Where((t, i) => '*' != t && t != text[i + offset]).Any())
return offset;
}
}
return -1;
}
which is a condensed and not really well-readable and maintainable version of:
private static int CheckTextContains(string text, string pattern)
{
if (text.Length >= pattern.Length) {
for (var offset = 0; offset < text.Length - pattern.Length; offset++) {
if (IsMatch(offset))
return offset;
}
}
return -1;
bool IsMatch(int offset) {
for (var i = 0; i < pattern.Length; i++)
{
if ('*' != pattern[i] && pattern[i] != text[i+offset]))
return false;
}
return true;
}
}
Upvotes: 0