Complex "Contains" string comparison - c#

I'm developing a C# 4.5 app and I need a function to return true for the following comparison:
"bla LéOnAr d/o bla".ComplexContains("leonardo")
In other words, I need string.Compare(str1, str2, CultureInfo.InvariantCulture, CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace) to also check for "contains!
Can anyone help?

You could use an appropriate CompareInfo and then CompareInfo.IndexOf(string, string, CompareOptions) and check the result against -1. Sample:
using System;
using System.Globalization;
class Program
{
static void Main()
{
var compareInfo = CultureInfo.InvariantCulture.CompareInfo;
var options = CompareOptions.IgnoreCase |
CompareOptions.IgnoreSymbols |
CompareOptions.IgnoreNonSpace;
var haystack = "bla Lé OnAr d/o bla";
var needle = "leonardo";
var index = compareInfo.IndexOf(haystack, needle, options);
Console.WriteLine(index); // 4
}
}
Or in method form:
private static bool ComplexContains(string source, string value)
{
var index = CultureInfo.InvariantCulture.CompareInfo.IndexOf
(source, value, CompareOptions.IgnoreCase |
CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace);
return index != -1;
}

Related

C# Convert string to if condition

Is it possible to convert string
"value > 5 && value <= 10"
to if statement?
if (value > 5 && value <= 10)
{
//do something
}
I have conditions stored as strings in DB, so it must be a dynamic conversion
Instead you can treat it as a javascript line and can get this done using Windows Script Engines, provided value is a real value instead of variable name.
if(ScriptEngine.Eval("jscript", "value > 5 && value <= 10"))
{
//Your logic
}
Or if its a variable then you can build a JS function like below to accomplish this:
using (ScriptEngine engine = new ScriptEngine("jscript"))
{
string JSfunction = "MyFunc(value){return " + "value > 5 && value <= 10" + "}";
ParsedScript parsed = engine.Parse(JSfunction);
if(parsed.CallMethod("MyFunc", 3))
{
// Your Logic
}
}
Use Compute method of System.Data.DataTable:
static class ExpressionHelper
{
private static readonly DataTable dt = new DataTable();
private static readonly Dictionary<string, string> expressionCache = new Dictionary<string, string>();
private static readonly Dictionary<string, object> resultCache = new Dictionary<string, object>();
// to be amended with necessary transforms
private static readonly (string old, string #new)[] tokens = new[] { ("&&", "AND"), ("||", "OR") };
public static T Compute<T>(this string expression, params (string name, object value)[] arguments) =>
(T)Convert.ChangeType(expression.Transform().GetResult(arguments), typeof(T));
private static object GetResult(this string expression, params (string name, object value)[] arguments)
{
foreach (var arg in arguments)
expression = expression.Replace(arg.name, arg.value.ToString());
if (resultCache.TryGetValue(expression, out var result))
return result;
return resultCache[expression] = dt.Compute(expression, string.Empty);
}
private static string Transform(this string expression)
{
if (expressionCache.TryGetValue(expression, out var result))
return result;
result = expression;
foreach (var t in tokens)
result = result.Replace(t.old, t.#new);
return expressionCache[expression] = result;
}
}
Usage
var expr = "value > 5 && value <= 10";
var a1 = expr.Compute<bool>(("value", 5)); // false
var a2 = expr.Compute<bool>(("value", 7)); // true
var a3 = expr.Compute<bool>(("value", 11)); // false
You could use Linq.Expression to build up the expression tree, the one you provided:
"value > 5 && value <= 10"
var val = Expression.Parameter(typeof(int), "x");
var body = Expression.And(
Expression.MakeBinary(ExpressionType.GreaterThan, val, Expression.Constant(5)),
Expression.MakeBinary(ExpressionType.LessThanOrEqual, val, Expression.Constant(10)));
var lambda = Expression.Lambda<Func<int, bool>>(exp, val);
bool b = lambda.Compile().Invoke(6); //true
bool b = lambda.Compile().Invoke(11); //false
This is just an example to get some idea, still you need a smart way to parse and build the tree.
I am afraid that you will have to create the simple parser for that.
You can try use something like FParsec. This is an F# parser library. I am not aware of such code in C#

C# Regex ask for "Parameter" and return "Value" for command line parse

Is it possible create Regex pattern for parse command line request?
Like:something text -PARAMETER="Hello world" something else.. -PARAMETER=HelloWorld
I want to ask for -PARAMETER and get two items "Hello world" (OR just Hello world) and second occurrence HelloWorld
Thanks
EDIT:
Sorry for an austere question. My question is what is the best method of Regex or something better for this problem.
In regex I used this pattern: -PARAMETER=".*"|-PARAMETER=.*?\s , but I mean that can be better.
One of possible method:
string command = "PARAMETER";
string pattern = $"-{command}= \".*\" | -{command}=.*?\\s";
foreach (Match _m in Regex.Matches(CommandBlockArgs, pattern, RegexOptions.IgnoreCase))
{
Console.WriteLine(_m.Value.Replace($"-{command}=", "").Replace("\"", "").Trim());
}
// *sorry for possible error, I wrote code now by head.*
But, also I mean that this is not nice solution.
For example, for me nice method for parse command by quotation marks is:
[DllImport("shell32.dll", SetLastError = true)]
static extern IntPtr CommandLineToArgvW(
[MarshalAs(UnmanagedType.LPWStr)] string lpCmdLine, out int pNumArgs);
public static string[] CommandLineToArgs(string commandLine)
{
int argc;
var argv = CommandLineToArgvW(commandLine, out argc);
if (argv == IntPtr.Zero)
throw new System.ComponentModel.Win32Exception();
try
{
var args = new string[argc];
for (var i = 0; i < args.Length; i++)
{
var p = Marshal.ReadIntPtr(argv, i * IntPtr.Size);
args[i] = Marshal.PtrToStringUni(p);
}
return args;
}
finally
{
Marshal.FreeHGlobal(argv);
}
}
But here is not my commands list. And Regex can be better.. Or combination?
*Sorry for English =)
So, my solution (C# 5.0):
internal static object CallMethod(Type type, string method, string parameters)
{
MethodInfo m = type.GetMethod(method, BindingFlags.Static | BindingFlags.Public);
ParameterInfo[] p = m.GetParameters();
object[] param = new object[p.Count()];
UInt16 i = 0;
foreach (ParameterInfo pi in p)
{
string pattern = $"-{pi.Name}=\".*\"|-{pi.Name}=.*?\\s"; // -paramName=paramValue || -paramName="param Value"
foreach (Match _m in Regex.Matches(parameters, pattern, RegexOptions.IgnoreCase))
{
Type paramType = pi.ParameterType;
string value = Regex.Replace(_m.Value, $"-{pi.Name}=", "", RegexOptions.IgnoreCase).Replace("\"", "").Trim();
param[i] = Convert.ChangeType(value, pi.ParameterType);
}
i++;
}
return m.Invoke(null, param);
}
It works, but maybe could be better.

Iterate Collection and Assign Each to Single Property with OR Operator

For example, this SecurityProtocol property can be assigned using an OR operator:
System.Net.ServicePointManager.SecurityProtocol =
SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
Now if we wanted to not hard code this assignment in the application by moving it to AppSettings as a comma-separated string like "192,768,3072", how would we convert the string to it's enumeration and assign it to a property using an OR operator?
[Flags]
public enum E
{
Foo = 1 << 2,
Bar = 1 << 4,
Baz = 1 << 9,
Planxty = Foo | Bar | Baz
}
...
var s = "16,4,512";
E enumresult =
// Split string by commas...
s.Split(',')
// Parse each numeric substring in turn and cast the result to the enum type...
.Select(nstr => (E)Int32.Parse(nstr))
// bitwise or each succeeding value against the rest
.Aggregate((a, b) => a | b);
You can cast an integer directly to an enum value, provided it's a valid value for that enum type. This will work fine:
var x = (E)(4 | 512);
You can split it by commas and apply | in a loop to parsed value array you have got from split:
string v = "192,768,3072";
string[] vals = v.Split(',');
var result = (SecurityProtocolType)int.Parse(vals[0]);
for (int i = 1; i < vals.Length; i++)
result = result | (SecurityProtocolType)int.Parse(vals[i]);
Here is the DEMO
You can create a function for it like"
public static SecurityProtocolType GetProtocolType(string v)
{
string[] vals = v.Split(',');
var result = (SecurityProtocolType)int.Parse(vals[0]);
for (int i = 1; i < vals.Length; i++)
result = result | (SecurityProtocolType)int.Parse(vals[i]);
return result;
}
and use it like:
GetProtocolType("192,3072");
DEMO

How do you test an enum flag combination?

Let’s say I have an enum flag:
[Flags]
public enum ColorType
{
None = 0,
Red = 1 << 0,
White = 1<<1,
Yellow = 1 << 2,
Blue = 1 << 3,
All = Red | White | Yellow | Blue
}
I have the below function, which parameter is a combination of flag, such as DoSomething( ColorType.Blue | ColorType.Yellow ).
public void DoSomethingr(ColorType theColorTypes)
{
if (theColorTypes.HasFlag(All)) Foo1();
if (theColorTypes.HasFlag(White) && theColorTypes.HasFlag(Red) ) Foo2();
if (!theColorTypes.HasFlag(Blue)) Foo3();
. . .
}
Is there an easy way to test all of possible flag bitwise combination?
[Test]
public void Test1(ColorType.Red | ColorType.Yellow | ColorType.White)
[Test]
public void Test1(ColorType.Red | ColorType.Yellow | ColorType.white | ColorType.Blue)
Thanks
Loop over all the possible values and put it in a TestCaseSource to generate a different test for each enumeration value:
public IEnumerable<ColorType> TestCaseSource
{
get
{
int start = (int)ColorType.None;
int count = (int)ColorType.All - start + 1;
return Enumerable.Range(start, count).Select(i => (ColorType)i);
}
}
[TestCaseSource("TestCaseSource")]
public void Test1(ColorType colorType)
{
// whatever your test is
}
Just my two cents and this could probably be improved to accept 'other' value types as well, but as an alternative when you like extension methods:
public static class EnumExtensions
{
public static bool HasFlags<TEnum>(this TEnum #enum,
TEnum flag,
params TEnum[] flags)
where TEnum : struct
{
var type = typeof(TEnum);
if (!type.IsEnum)
throw new ArgumentException("#enum is not an Enum");
var hasFlagsMethod = type.GetMethod("HasFlag");
var hasFlag = new Func<TEnum, bool>(e =>
{
return (bool)hasFlagsMethod.Invoke(#enum, new object[] { e });
});
// test the first flag argument
if (!hasFlag(flag))
return false;
// test the params flags argument
foreach (var flagValue in flags)
{
if (!hasFlag(flagValue))
return false;
}
return true;
}
}
[Flags]
public enum ColorType
{
None = 0,
Red = 1 << 0,
White = 1 << 1,
Yellow = 1 << 2,
Blue = 1 << 3,
All = Red | White | Yellow | Blue
}
Call it like this:
class Program
{
static void Main(string[] args)
{
var color = ColorType.Red;
Console.WriteLine(color.HasFlags(ColorType.Red)); // true;
Console.WriteLine(color.HasFlags(ColorType.Red, ColorType.Blue)); // false;
color = ColorType.All;
Console.WriteLine(color.HasFlags(ColorType.Red, ColorType.Blue)); // true;
Console.ReadLine();
}
}

How to convert a string with comma to integer in C#

For example I have a string "99,999 ABC XYZ"
Now I want to convert to integer "99999"
I have made a code that worked:
var regex = new Regex("[A-z]");
linksOnPage = regex.Replace(linksOnPage, "");
regex = new Regex(" ");
linksOnPage = regex.Replace(linksOnPage, "");
int noPage = int.Parse(regex.Replace(linksOnPage, ""),
NumberStyles.AllowThousands);
But I feel it's not good enough, can anyone help me to make it shorter?
This Regex will remove all the letters and spaces:
var regex = new Regex(" |[A-z]");
linksOnPage = regex.Replace(linksOnPage, "");
You could use int.Parse and add the NumberStyles.AllowThousands flag:
int num = int.Parse(linksOnPage , NumberStyles.AllowThousands);
Or int.TryParse letting you know if the operation succeeded:
int num;
if (int.TryParse(linksOnPage , NumberStyles.AllowThousands,
CultureInfo.InvariantCulture, out num))
{
// parse successful, use 'num'
}
Or you can also try this:
int num = int.Parse(linksOnPage.Replace(",", ""));
This would be my approach, it's not really shorter though:
public static void Main(string[] args)
{
string input = "99,999 ABC XYZ";
var chars = input.ToCharArray().ToList();
var builder = new StringBuilder();
foreach (var character in chars)
{
if (char.IsNumber(character))
builder.Append(character);
}
int result = 0;
int.TryParse(builder.ToString(), out result);
Console.WriteLine(result);
Console.ReadKey();
}
you could do something like this:
int? ParseIntFromString( string s )
{
Match m = rxInt.Match(s) ;
int? value = m.Success ? int.Parse( m.Value , NumberStyles.AllowLeadingSign|NumberStyles.AllowThousands ) : (int?)null ;
return value ;
}
static Regex rxInt = new Regex( #"^-?\d{1,3}(,\d\d\d)*");
the return value is null if no integer value was found and the parsed value if it was.
Note that the match is anchored at start-of-string via ^; if you want to match an number anywhere in the string, simply remove it.
You could also kick it old-school and do something like this:
public int? ParseIntFromString( string s )
{
bool found = false ;
int acc = 0 ;
foreach( char c in s.Where( x => char.IsDigit )
{
found = true ;
acc += c - '0' ;
}
return found ? (int?)acc : (int?) null ;
}

Categories