Can you please help me to write regular expression for this.
Name = "Windows Product for .Net"
Type = "Software Programming"
Quantity = "Pack of 3"
I want to do a match like this in c# for which I need RegEx.
If Name.contains(".Net") && (Type.Contains("Programming") || Type.Contains("Hardware")
{
// output will be a Match.
}
else
{
// no match.
}
The approach I want to take here is , specify regular expression for each condition and then apply logical operand && , logical grouping paranthesis and then logical operand ||.
I have come up with all regular expressions for these. How can I provide logical operands for each of them to execute in appropriate order?
string Name = "Windows Product for .Net";
string Type = "Software Programming";
string patternForName = ".*Net";
Regex rgxName = new Regex(patternForName);
Match matchName = rgx.Match(Name);
string patternForType = ".*Programming";
Regex rgxType = new Regex(patternForType);
Match matchType = rgx.Match(Type);
string patternForType1 = ".*Hardware";
Regex rgxType1 = new Regex(patternForType1);
Match matchType1 = rgx.Match(Type);
Please note - We are making it dynamic, in the sense the patterns , operands and regEx are coming from xml file. So that's why I do not want to write one big regEx for above.
First of all you don't need a leading .* in your expression unless you want the whole match (i.e. when working with matches). Just for a simple "is it there" you won't need it as the pattern might match any position.
Just use one regular expression for each field (i.e. one for Name, one for Type, one for Quantity:
string patternForName = "\\.Net"; // escaping the dot so it will match real dots only
string patternForType = "Programming|Hardware"; // | will result in "left side or right side"
string patternForQuantity = ".?"; // will match any string, even empty ones
To check everything:
bool match = rgxName.IsMatch(Name) && rgxType.IsMatch(Type) && rgx.IsMatch(Quantity);
You can make them dynamic without using regex. Using regex won't really save you any time or effort, since the code's going to be about the same size either way. Following your pattern above, you can do something like this:
var names = new[] { "Net", "Programming" };
var types = new[] { "Hardware" };
bool matchFound = true;
foreach (string n in names)
matchFound &= Name.Contains(n);
foreach (string t in types)
matchFound |= Type.Contains(t);
The above code assumes you want to match all of "names" and any of "types", but you can substitute any logic you want.
The real crux of your problem is these boolean combinations; regex won't help you with the logic for those, so you're better off using string.Contains unless the patterns you're looking for become much more variable. Regex is distracting you from your real goal here, in my opinion.
It sounds like you're asking how you should handle the logical part of the problem. If you're pulling it from an xml file, you could structure your file in the way you want to structure your logic.
for example, have And and Or groups:
<And>
<Name Match=".Net"/>
<Or>
<Type Match="Programming"/>
<Type Match="Hardware"/>
</Or>
</And>
Create classes for each of these types. For brevity, I didnt define the classes with properties or create constructors, but you can fill them out however you want:
class YourType
{
string Name;
string Type;
string Quantity;
}
abstract class Test
{
public abstract bool RunTest(YourType o);
}
class AndTest : Test
{
public List<Test> Children;
public bool RunTest(YourType o)
{
foreach (var test in Children)
{
if (!test.RunTest(o)) return false;
}
return true;
}
}
class OrTest : Test
{
public List<Test> Children;
public bool RunTest(YourType o)
{
foreach (var test in Children)
{
if (test.RunTest(o)) return true;
}
return false;
}
}
class NameTest : Test
{
public string Match;
public bool RunTest(YourType o)
{
return o.Name.Contains(Match);
}
}
class TypeTest : Test
{
public string Match;
public bool RunTest(YourType o)
{
return o.Type.Contains(Match);
}
}
Build the class structure from the xml file and just call RunTest from the top level Test. This way you can do any type of logic youd like. I just used Contains instead of a Regex for ease of the example, but you can easily replace the string match with a regex match.
if (rgxName.IsMatch(Name) && (rgxType.IsMatch(Type) || rgxType1.IsMatch(Type))
{
...
}
In .NET, Regex.Match matches anywhere in the string, so you don't need the any-characters (.*) prefix on your pattern. So, to check for ".NET", it would simply be:
Regex regexName = new Regex(#"\.NET", RegexOptions.IgnoreCase);
// IsMatch returns true/false, Match returns a Match object
bool nameMatches = regexName.IsMatch(name);
Your patterns for Programming and Hardware would just be
new Regex(#"Programming", RegexOptions.IgnoreCase) // Or leave out IgnoreCase if you're case-sensitive
new Regex(#"Hardware")
If you have a list of Name patterns and a list of type patterns, you could do something similar to this:
bool nameIsMatch = false;
bool typeIsMatch = false;
foreach (string namePattern in namePatterns)
{
nameIsMatch = nameIsMatch || Regex.IsMatch(nameString, namePattern);
}
foreach (string typePattern in typePatterns)
{
typeIsMatch = typeIsMatch || Regex.IsMatch(typeString, typePattern);
}
if (nameIsMatch && typeIsMatch)
{
// Whatever you want to do
}
patternForName = ".Net"
patternForType = "Programming"
patternForType1 = "Hardware"
You might find The Regex Coach to be useful.
Related
I have a string aaaaabbbbbccccc I have a dictionary that has certain rules
Dictionary<string, string> rules = new Dictionary<string, string>();
rules.Add("abc", "aab");
rules.Add("ac", "ba");
rules.Add("cb", "cc");
This means if string has abc it will be replaced with aab but string doesn't have any rules that match. So, I am creating a new string out of the old string based on these rules. For instance, if I rearrange the old string to abcabcabcabcabc then rule can be applied. But I am stuck at rearranging. I tried using IndexOf and Remove functions but I didn't get the positive output. This is my code
string s;
s = "aaaaabbbbbccccc";
string newString = "";
int ia, ib, ic;
//Formulating rule 1
if (s.Contains("a") && s.Contains("b") && s.Contains("c"))
{
ia = s.IndexOf("a");
ib = s.IndexOf("b");
ic = s.IndexOf("c");
if (ia < ib && ib < ic)
{
newString += "abc";
s.Remove(ia, 1);
s.Remove(ib, 1);
s.Remove(ic, 1);
}
}
Console.WriteLine("New String " + newString);
Console.WriteLine("Old String " + s);
I am getting
New String abc
Old String aaaaabbbbbccccc //Which is wrong.
Can anyone help what I am doing wrong or is there any better way?
Your explanation does not match your code.
In your explanation, you say that aaaaabbbbbccccc does not match any rule (e.g. the "abc" rule). However, in your code, you are not checking if it contains "abc", but rather that it contain "a" and "b" and "c", not necessarily as a single chunk:
if (s.Contains("a") && s.Contains("b") && s.Contains("c"))
This is an important difference:
aaaaabbbbbccccc does not contain abc (rule does not apply)
aaaaabbbbbccccc does contain a, b and c (rule does apply)
You're contradicting yourself. Which is correct here, the code or your explanation? Does the rule check for the exact string, or does it check for all characters separately?
Awkward string manipulation.
Based on your code; I infer that you're not experienced with some very common string operations (no offense intended). Unless you contradict me, I'm going to assume that your explanation is correct and your code is not.
Checking if a string contains a substring:
As I explained previously, there is an important difference between checking is a string contains a substring:
s.Contains("abc") //1
and checking if a string contains each individual character of a substring:
s.Contains("a") && s.Contains("b") && s.Contains("c") //2
As a more practical example; does my username (Flater) contain the substring "Fertla"?
If you use the logic in //1, the answer is no.
If you use the logic in //2, the answer is yes.
Based on your explanation, you should be using //1
Replacing a string:
This means if string has abc it will be replaced with aab
There is a very simple method for this:
s.Replace("abc", "aab");
Some examples:
abcdef becomes aabdef
abcabcabc becomes aabaabaab (it replaces all occurrences)
uvwxyz becomes uvwxyz (if it doesn't occur, nothing gets replaced)
Take note of the second and third bullet point.
String.Replace() will replace all occurrences, in case the substring occurs more than once. Based on your explanation, I assume this is what you want (if it's possible for a substring to occur more than once, to begin with).
If the substring is not found, String.Replace() will give you the same output as its input. Nothing will be changed. This means that you can execute your rule on your string without needing to check if the substring exists:
If it does exist, then your value will be replaced; just like you want it to happen.
If it does not exist, then nothing will happen; which is also what you want to happen.
You can dramatically simplify your code!
Create your dictionary;
Dictionary<string, string> rules = new Dictionary<string, string>();
rules.Add("abc", "aab");
rules.Add("ac", "ba");
rules.Add("cb", "cc");
Define your string:
string s = "aaaaaaabbbbbbccccccc";
And the rest is easy enough:
foreach(var rule in rules)
{
s = s.Replace(rule.Key, rule.Value);
}
This will try to perform a replace for every rule that you've defined. If it finds rules that are applicable; then it will replace the values.
Note
This code assumes that your replace values do not collide. If you do want to avoid collisions, you will have to check if substrings of all defined rules exist (Contains()) before actually replacing a value.
I have a really hard time understanding you requierment, but here is a solution may you tell me if this is even close to what you want?
private static string WeirdArrangement (string input)
{
//string input = "aabbcc[aczç_eyvur]";
string validChars = "abc";
string pattern = "abc"; // Must be a composition of all valid char
var invalidChars = input.Where(c => !validChars.Contains(c));
var validOccurences = input.Where(c => validChars.Contains(c))
.GroupBy(c => c)
.Select(c => new { Char = c.Key, Count = c.Count() });
var minPattern = validOccurences.Min(o => o.Count);
// Build time
StringBuilder builder = new StringBuilder();
//new StringBuilder(pattern.Length * minPattern + invalidChars.Count() + leftoverCount);
// X time partern
for (int i = 0; i < minPattern; i++) builder.Append(pattern);
//Rest of the validOccurences
foreach (var charOccurency in validOccurences)
{
for (int i = minPattern; i < charOccurency.Count; i++) builder.Append(charOccurency.Char);
}
//Invalid char
foreach (var invalidChar in invalidChars)
{
builder.Append(invalidChar);
};
return builder.ToString();
}
I'm accepting an input string that I want to be a ternary statement that works on strings. So my method signature would look like this:
public string Parse(string value, string ternaryStatement)
and there parameters would give these results:
Parse(null, "=?It's Null:It's Not Null") == "It's Null" // Empty string would
Parse("", "=?It's Null:It's Not Null") == "It's Null" // be same as null
This example is fairly simple, Split the string first by '?' then by ':'
But of course I need a method to handle escape characters, "\", "\?" and ":", where "\" is valid anywhere, "\?" would only be valid before the first unescaped "?" and ":" would only be valid after that same "?".
Parse(#"\?\", #"=\\\?\\?\:Match\::\:No Match\:") == ":Match:"
Parse(#"\?\", #"!=\\\?\\?\:No Match\::\:Match\:") == ":Match:"
But this is really complicated. I believe I can perform it using regular expressions, but that just creates another problem since this is well beyond my limited understanding of regular expressions. What's the best way to tackle this problem?
Edit 1
Some of the background: I'm storing a format for a URL in a database config table (It's actually Dynamics 365 for Customer Engagement, but that doesn't matter at this point). The format is stored as strings, and the parameters that are required are defined in code. So generally it looks like this:
Format: "https://something.com?Foo={0}&Bar={1}"
Description: "0 - Foo, 1 - Bar"
where the description is used both for the person that is formatting the url, and the developer that needs to know how to structure the format statement.
The problem I'm running into right now is that I have a url that requires at least one of two different parameters. If one of the values is null or empty, it will error if included in the url. So I need a way of saying, if Foo is null or Bar is null, don't include the name or &. Ideally I'd like to implement this like this:
"https://something.com?{0:=?:Foo={{0}}}&}{1:=?:Bar={{1}}}}"
So if Foo is null and Bar is "Bar" the output would be
"https://something.com?Bar=Bar"
I could also see this being used if we need to switch between a 0/1 for a boolean to true/false without having to change code:
"https://something.com?{0:=0?false:true}"
The two regexes should be:
Regex rx = new Regex(#"(?<=(?:^|[^\\])(?:\\\\)*)\?");
Regex rx2 = new Regex(#"(?<=(?:^|[^\\])(?:\\\\)*):");
Use them like:
var m = rx.Match(str);
if (m.Success)
{
int ix = m.Index;
}
The main point of the two rx is that the searched string (\? or :) must be preceded by
(?<=(?:^|[^\\])(?:\\\\)*)
that is the beginning of the string ^ or not a \ ([^\\]) plus zero or an even number of \\ that is (?:\\\\)*.
A all-in-one regex is:
Regex rx = new Regex(#"^(?<operator>=|!=|<=|>=|<|>)(?<cmp>(?:(?:\\.)|[^?:])*)\?(?<true>(?:(?:\\.)|[^?:])*):(?<false>(?:(?:\\.)|[^?:])*)$");
if (m.Success)
{
string op = m.Groups["operator"].Value;
string cmp = m.Groups["cmp"].Value;
string true1 = m.Groups["true"].Value;
string false1 = m.Groups["false"].Value;
}
In op you'll get the comparison operator used, in cmp the comparand, in true1 and false1 the true and false strings. If !m.Success then the string isn't correctly formatted. Comprehending the regex is left as a simple exercise for the reader (unless you comprehend a regex, you shouldn't ever use it, because before or later you'll have to modify it/fix it/debug it)
Solution to returning different values based on input string
Why do you need to pass in a ternary statement / wouldn't this make more sense?
string Parse(string value, string returnIfNull, string returnIfNotNull)
{
return string.IsNullOrEmpty(value) ? returnIfNull: returnIfNotNull;
}
Console.WriteLine(Parse("", "treat as null", "not expected"));
Console.WriteLine(Parse("hello", "not expected", "this value's not null"));
Parsing a ternary string for values
However, if you really need to do this, you could use something like the below:
private static readonly Regex TernaryParserRegex = new Regex(
#"^=\?(?<ifNull>(?:\\(\\\\)*:|[^:])*)(?<!\\):(?<ifNotNull>(?:\\(\\\\)*:|[^:])*)$"
/* , RegexOptions.Compiled //include this line for performance if appropriate */
);
private string[] ParseTernaryString (string ternaryStatement)
{
var results = TernaryParserRegex.Match(ternaryStatement);
if (results.Success)
{
string[] returnVal = {
results.Groups["ifNull"].Value
,
results.Groups["ifNotNull"].Value
};
return returnVal;
}
else
{
throw new Exception("Invalid Ternary Statement"); //use an appropriate exception type here; or have the function return `{null,null}` / some other default as appropriate
}
}
public string Parse(string value, string ternaryStatement)
{
var returnValues = ParseTernaryString(ternaryStatement);
return string.IsNullOrEmpty(value) ? returnValues[0]: returnValues[1];
}
//Example Usage:
Console.WriteLine(Parse("", "=?treat as null:not expected"));
Console.WriteLine(Parse("hello", "=?not expected:this value's not null"));
An explanation of the regex & additional examples are available here:
https://regex101.com/r/YJ9qd3/1
Appending non-null/blank values to a URL's Query String
public void Main()
{
var url = "https://example.com?something=keepMe&foo=FooWasHere&bar=swapMeQuick";
var dict = new System.Collections.Generic.Dictionary<string, string>();
dict.Add("foo", null);
dict.Add("bar", "hello");
dict.Add("boo", "new");
Console.WriteLine(CreateUri(url, dict).ToString());
}
Uri CreateUri(string uri, IDictionary<string, string> parameters)
{
return CreateUri(new Uri(uri), parameters);
}
Uri CreateUri(Uri uri, IDictionary<string, string> parameters)
{
var query = HttpUtility.ParseQueryString(uri.Query); //https://msdn.microsoft.com/en-us/library/ms150046(v=vs.110).aspx; though returns HttpValueCollection
foreach (string key in parameters.Keys)
{
if (string.IsNullOrEmpty(parameters[key]))
{ //parameter is null or empty; if such a parameter already exists on our URL, remove it
query.Remove(key); //if this parameter does not already exist, has no effect (but no exception is thrown)
}
else
{ //parameter has a value; add or update the query string with this value
query.Set(key, parameters[key]);
}
}
var builder = new UriBuilder(uri);
builder.Query = query.ToString();
return builder.Uri;
}
I have a rules engine that takes a string as a name of a rule, and compares it to a string, predicate dictionary. I'm writing a rule that will compare two datetimes and return true if they match, but allow a windows of a configurable number of seconds. Ideally, I'd like for my string/predicate key value pair to look something like
{"Allow <x> seconds", AllowXSeconds}
A user applying the rule would decide they would like a 10 second window on either side of the original datetime so they would say "Allow 10 seconds" in config. I want my code to be able to to recognize that the user wants to apply the "Allow seconds" rule, then pull the "10" out so I can use it as a variable in the rule's logic. I'd rather not use regex, as the class is already built, and I don't know if I'll be allowed to refactor it in that way. I will, though, if no one has any clever ideas on how to do this. Thanks in advance, to all you smart guys and gals!
You can validate using string.StartsWith and string.EndsWith then use string.Substring to get the desired value and int.TryParse to attempt to parse the value and validate that it is an integer
string str = "Allow 10 seconds";
int result;
if (str.StartsWith("Allow ")
&& str.EndsWith(" seconds")
&& int.TryParse(str.Substring(6, str.Length - 14), out result))
{
Console.WriteLine(result);
}
else
{
Console.WriteLine("Invalid");
}
Also if desired there are overloads of StartsWith and EndsWith that will allow for case insensitive matching as well.
This looks like a perfect candidate for regular expressions.
Here is a LINQPad program that demonstrates:
void Main()
{
var lines = new[]
{
"Allow 10 seconds",
"Allow 5 seconds",
"Use 5mb"
};
var rules = new Rule[]
{
new Rule(
#"^Allow\s+(?<seconds>\d+)\s+seconds?$",
ma => AllowSeconds(int.Parse(ma.Groups["seconds"].Value)))
};
foreach (var line in lines)
{
bool wasMatched = rules.Any(rule => rule.Visit(line));
if (!wasMatched)
Console.WriteLine($"not matched: {line}");
}
}
public void AllowSeconds(int seconds)
{
Console.WriteLine($"allow: {seconds} second(s)");
}
public class Rule
{
public Rule(string pattern, Action<Match> action)
{
Pattern = pattern;
Action = action;
}
public string Pattern { get; }
public Action<Match> Action { get; }
public bool Visit(string line)
{
var match = Regex.Match(line, Pattern);
if (match.Success)
Action(match);
return match.Success;
}
}
Output:
allow: 10 second(s)
allow: 5 second(s)
not matched: Use 5mb
Is there a call in .NET that parses the CN from a rfc-2253 encoded distinguished name? I know there are some third-party libraries that do this, but I would prefer to use native .NET libraries if possible.
Examples of a string encoded DN
CN=L. Eagle,O=Sue\, Grabbit and Runn,C=GB
CN=Jeff Smith,OU=Sales,DC=Fabrikam,DC=COM
If you are working with an X509Certificate2, there is a native method that you can use to extract the Simple Name. The Simple Name is equivalent to the Common Name RDN within the Subject field of the main certificate:
x5092Cert.GetNameInfo(X509NameType.SimpleName, false);
Alternatively, X509NameType.DnsName can be used to retrieve the Subject Alternative Name, if present; otherwise, it will default to the Common Name:
x5092Cert.GetNameInfo(X509NameType.DnsName, false);
After digging around in the .NET source code it looks like there is an internal utility class that can parse Distinguished Names into their different components. Unfortunately the utility class is not made public, but you can access it using reflection:
string dn = "CN=TestGroup,OU=Groups,OU=UT-SLC,OU=US,DC=Company,DC=com";
Assembly dirsvc = Assembly.Load("System.DirectoryServices");
Type asmType = dirsvc.GetType("System.DirectoryServices.ActiveDirectory.Utils");
MethodInfo mi = asmType.GetMethod("GetDNComponents", BindingFlags.NonPublic | BindingFlags.Static);
string[] parameters = { dn };
var test = mi.Invoke(null, parameters);
//test.Dump("test1");//shows details when using Linqpad
//Convert Distinguished Name (DN) to Relative Distinguished Names (RDN)
MethodInfo mi2 = asmType.GetMethod("GetRdnFromDN", BindingFlags.NonPublic | BindingFlags.Static);
var test2 = mi2.Invoke(null, parameters);
//test2.Dump("test2");//shows details when using Linqpad
The results would look like this:
//test1 is array of internal "Component" struct that has name/values as strings
Name Value
CN TestGroup
OU Groups
OU UT-SLC
OU US
DC company
DC com
//test2 is a string with CN=RDN
CN=TestGroup
Please not this is an internal utility class and could change in a future release.
I had the same question, myself, when I found yours. Didn't find anything in the BCL; however, I did stumble across this CodeProject article that hit the nail squarely on the head.
I hope it helps you out, too.
http://www.codeproject.com/Articles/9788/An-RFC-2253-Compliant-Distinguished-Name-Parser
Do Win32 functions count? You can use PInvoke with DsGetRdnW. For code, see my answer to another question: https://stackoverflow.com/a/11091804/628981.
You can extract the common name from an ASN.1-encoded distinguished name using AsnEncodedData class:
var distinguishedName= new X500DistinguishedName("CN=TestGroup,OU=Groups,OU=UT-SLC,OU=US,DC=Company,DC=com");
var commonNameData = new AsnEncodedData("CN", distinguishedName.RawData);
var commonName = commonNameData.Format(false);
A downside of this approach is that if you specify an unrecognized OID or the field identified with the OID is missing in the distinguished name, Format method will return a hex string with the encoded value of full distinguished name so you may want to verify the result.
Also the documentation does not seem to specify if the rawData parameter of the AsnEncodedData constructor is allowed to contain other OIDs besides the one specified as the first argument so it may break on non-Windows OS or in a future version of .NET Framework.
If you are on Windows, #MaxKiselev's answer works perfectly. On non-Windows platforms, it returns the ASN1 dumps of each attribute.
.Net Core 5+ includes an ASN1 parser, so you can access the RDN's in a cross-platform manner by using AsnReader.
Helper class:
public static class X509DistinguishedNameExtensions
{
public static IEnumerable<KeyValuePair<string, string>> GetRelativeNames(this X500DistinguishedName dn)
{
var reader = new AsnReader(dn.RawData, AsnEncodingRules.BER);
var snSeq = reader.ReadSequence();
if (!snSeq.HasData)
{
throw new InvalidOperationException();
}
// Many types are allowable. We're only going to support the string-like ones
// (This excludes IPAddress, X400 address, and other wierd stuff)
// https://www.rfc-editor.org/rfc/rfc5280#page-37
// https://www.rfc-editor.org/rfc/rfc5280#page-112
var allowedRdnTags = new[]
{
UniversalTagNumber.TeletexString, UniversalTagNumber.PrintableString,
UniversalTagNumber.UniversalString, UniversalTagNumber.UTF8String,
UniversalTagNumber.BMPString, UniversalTagNumber.IA5String,
UniversalTagNumber.NumericString, UniversalTagNumber.VisibleString,
UniversalTagNumber.T61String
};
while (snSeq.HasData)
{
var rdnSeq = snSeq.ReadSetOf().ReadSequence();
var attrOid = rdnSeq.ReadObjectIdentifier();
var attrValueTagNo = (UniversalTagNumber)rdnSeq.PeekTag().TagValue;
if (!allowedRdnTags.Contains(attrValueTagNo))
{
throw new NotSupportedException($"Unknown tag type {attrValueTagNo} for attr {attrOid}");
}
var attrValue = rdnSeq.ReadCharacterString(attrValueTagNo);
var friendlyName = new Oid(attrOid).FriendlyName;
yield return new KeyValuePair<string, string>(friendlyName ?? attrOid, attrValue);
}
}
}
Example usage:
// Subject: CN=Example, O=Organization
var cert = new X509Certificate2("foo.cer");
var names = this.cert.SubjectName.GetRelativeNames().ToArray();
// names has [ { "CN": "Example" }, { "O": "Organization" } ]
Since this does not involve any string parsing, no escape or injections can be mishandled. It doesn't support decoding DN's that contain non-string elements, but those seem exceedingly rare.
How about this one:
string cnPattern = #"^CN=(?<cn>.+?)(?<!\\),";
string dn = #"CN=Doe\, John,OU=My OU,DC=domain,DC=com";
Regex re = new Regex(cnPattern);
Match m = re.Match(dn);
if (m.Success)
{
// Item with index 1 returns the first group match.
string cn = m.Groups[1].Value;
}
Adapted from Powershell Regular Expression for Extracting Parts of an Active Directory Distiniguished Name.
Just adding my two cents here. This implementation works "best" if you first learn what business rules are in place that will ultimately dictate how much of the RFC will ever be implemented at your company.
private static string ExtractCN(string distinguishedName)
{
// CN=...,OU=...,OU=...,DC=...,DC=...
string[] parts;
parts = distinguishedName.Split(new[] { ",DC=" }, StringSplitOptions.None);
var dc = parts.Skip(1);
parts = parts[0].Split(new[] { ",OU=" }, StringSplitOptions.None);
var ou = parts.Skip(1);
parts = parts[0].Split(new[] { ",CN=" }, StringSplitOptions.None);
var cnMulti = parts.Skip(1);
var cn = parts[0];
if (!Regex.IsMatch(cn, "^CN="))
throw new CustomException(string.Format("Unable to parse distinguishedName for commonName ({0})", distinguishedName));
return Regex.Replace(cn, "^CN=", string.Empty);
}
You could use regular expressions to do this. Here's a regex pattern than can parse the whole DN, then you can just take the parts you are interested in:
(?:^|,\s?)(?:(?<name>[A-Z]+)=(?<val>"(?:[^"]|"")+"|(?:\\,|[^,])+))+
Here it is formatted a bit nicer, and with some comments:
(?:^|,\s?) <-- Start or a comma
(?:
(?<name>[A-Z]+)
=
(?<val>
"(?:[^"]|"")+" <-- Quoted strings
|
(?:\\,|[^,])+ <-- Unquoted strings
)
)+
This regex will give you name and val capture groups for each match.
DN strings can optionally be quoted (e.g. "Hello", which allows them to contain unescaped commas. Alternatively, if not quoted, commas must be escaped with a backslash (e.g. Hello\, there!). This regex handles both quoted and unquoted strings.
Here's a link so you can see it in action: https://regex101.com/r/7vhdDz/1
If the order is uncertain, I do this:
private static string ExtractCN(string dn)
{
string[] parts = dn.Split(new char[] { ',' });
for (int i = 0; i < parts.Length; i++)
{
var p = parts[i];
var elems = p.Split(new char[] { '=' });
var t = elems[0].Trim().ToUpper();
var v = elems[1].Trim();
if (t == "CN")
{
return v;
}
}
return null;
}
This is my almost RFC-compliant fail-safe DN parser derived from https://www.codeproject.com/Articles/9788/An-RFC-2253-Compliant-Distinguished-Name-Parser and an example of its usage (extract subject name as CN and O, both optional, concatenated with comma):
private static string GetCertificateString(X509Certificate2 certificate)
{
var subjectComponents = certificate.Subject.ParseDistinguishedName();
var subjectName = string.Join(", ", subjectComponents
.Where(m => (m.Item1 == "CN") || (m.Item1 == "O"))
.Select(n => n.Item2)
.Distinct());
return $"{certificate.SerialNumber} {certificate.NotBefore:yyyy.MM.dd}-{certificate.NotAfter:yyyy.MM.dd} {subjectName}";
}
private enum DistinguishedNameParserState
{
Component,
QuotedString,
EscapedCharacter,
};
public static IEnumerable<Tuple<string, string>> ParseDistinguishedName(this string value)
{
var previousState = DistinguishedNameParserState.Component;
var currentState = DistinguishedNameParserState.Component;
var currentComponent = new StringBuilder();
var previousChar = char.MinValue;
var position = 0;
Func<StringBuilder, Tuple<string, string>> parseComponent = sb =>
{
var s = sb.ToString();
sb.Clear();
var index = s.IndexOf('=');
if (index == -1)
{
return null;
}
var item1 = s.Substring(0, index).Trim().ToUpper();
var item2 = s.Substring(index + 1).Trim();
return Tuple.Create(item1, item2);
};
while (position < value.Length)
{
var currentChar = value[position];
switch (currentState)
{
case DistinguishedNameParserState.Component:
switch (currentChar)
{
case ',':
case ';':
// Separator found, yield parsed component
var component = parseComponent(currentComponent);
if (component != null)
{
yield return component;
}
break;
case '\\':
// Escape character found
previousState = currentState;
currentState = DistinguishedNameParserState.EscapedCharacter;
break;
case '"':
// Quotation mark found
if (previousChar == currentChar)
{
// Double quotes inside quoted string produce single quote
currentComponent.Append(currentChar);
}
currentState = DistinguishedNameParserState.QuotedString;
break;
default:
currentComponent.Append(currentChar);
break;
}
break;
case DistinguishedNameParserState.QuotedString:
switch (currentChar)
{
case '\\':
// Escape character found
previousState = currentState;
currentState = DistinguishedNameParserState.EscapedCharacter;
break;
case '"':
// Quotation mark found
currentState = DistinguishedNameParserState.Component;
break;
default:
currentComponent.Append(currentChar);
break;
}
break;
case DistinguishedNameParserState.EscapedCharacter:
currentComponent.Append(currentChar);
currentState = previousState;
currentChar = char.MinValue;
break;
}
previousChar = currentChar;
position++;
}
// Yield last parsed component, if any
if (currentComponent.Length > 0)
{
var component = parseComponent(currentComponent);
if (component != null)
{
yield return component;
}
}
}
Sorry for being a bit late to the party, but I was able to call the Name attribute directly from c#
UserPrincipal p
and then I was able to call
p.Name
and that gave me the full name (Common Name)
Sample code:
string name;
foreach(UserPrincipal p in PSR)
{
//PSR refers to PrincipalSearchResult
name = p.Name;
Console.WriteLine(name);
}
Obviously, you will have to fill in the blanks. But this should be easier than parsing regex.
Could you not just retrieve the CN attribute values?
As you correctly note, use someone else's class as there are lots of fun edge cases (escaped commas, escaped other characters) that make parsing a DN look easy, but actually reasonably tricky.
I usually use a Java class that comes with the Novell (Now NetID) Identity Manager. So that is not helpful.
using System.Linq;
var dn = "CN=Jeff Smith,OU=Sales,DC=Fabrikam,DC=COM";
var cn = dn.Split(',').Where(i => i.Contains("CN=")).Select(i => i.Replace("CN=", "")).FirstOrDefault();
Well, Here I am another person late to the party. Here is my Solution:
var dn = new X500DistinguishedName("CN=TestGroup,OU=Groups,OU=UT-SLC,OU=US,DC=\"Company, inc\",DC=com");
foreach(var part in dn.Format(true).Split("\r\n"))
{
if(part == "") continue;
var parts = part.Split('=', 2);
var key = parts[0];
var value = parts[1];
// use your key and value as you see fit here.
}
Basically its leveraging the X500DistinguishedName.Format method to put things on lines. Then split by lines, then split each line into key value.
How do I check a string to make sure it contains numbers, letters, or space only?
In C# this is simple:
private bool HasSpecialChars(string yourString)
{
return yourString.Any(ch => ! char.IsLetterOrDigit(ch));
}
The easiest way it to use a regular expression:
Regular Expression for alphanumeric and underscores
Using regular expressions in .net:
http://www.regular-expressions.info/dotnet.html
MSDN Regular Expression
Regex.IsMatch
var regexItem = new Regex("^[a-zA-Z0-9 ]*$");
if(regexItem.IsMatch(YOUR_STRING)){..}
string s = #"$KUH% I*$)OFNlkfn$";
var withoutSpecial = new string(s.Where(c => Char.IsLetterOrDigit(c)
|| Char.IsWhiteSpace(c)).ToArray());
if (s != withoutSpecial)
{
Console.WriteLine("String contains special chars");
}
Try this way.
public static bool hasSpecialChar(string input)
{
string specialChar = #"\|!#$%&/()=?»«#£§€{}.-;'<>_,";
foreach (var item in specialChar)
{
if (input.Contains(item)) return true;
}
return false;
}
String test_string = "tesintg#$234524##";
if (System.Text.RegularExpressions.Regex.IsMatch(test_string, "^[a-zA-Z0-9\x20]+$"))
{
// Good-to-go
}
An example can be found here: http://ideone.com/B1HxA
If the list of acceptable characters is pretty small, you can use a regular expression like this:
Regex.IsMatch(items, "[a-z0-9 ]+", RegexOptions.IgnoreCase);
The regular expression used here looks for any character from a-z and 0-9 including a space (what's inside the square brackets []), that there is one or more of these characters (the + sign--you can use a * for 0 or more). The final option tells the regex parser to ignore case.
This will fail on anything that is not a letter, number, or space. To add more characters to the blessed list, add it inside the square brackets.
Use the regular Expression below in to validate a string to make sure it contains numbers, letters, or space only:
[a-zA-Z0-9 ]
You could do it with a bool. I've been learning recently and found I could do it this way. In this example, I'm checking a user's input to the console:
using System;
using System.Linq;
namespace CheckStringContent
{
class Program
{
static void Main(string[] args)
{
//Get a password to check
Console.WriteLine("Please input a Password: ");
string userPassword = Console.ReadLine();
//Check the string
bool symbolCheck = userPassword.Any(p => !char.IsLetterOrDigit(p));
//Write results to console
Console.WriteLine($"Symbols are present: {symbolCheck}");
}
}
}
This returns 'True' if special chars (symbolCheck) are present in the string, and 'False' if not present.
A great way using C# and Linq here:
public static bool HasSpecialCharacter(this string s)
{
foreach (var c in s)
{
if(!char.IsLetterOrDigit(c))
{
return true;
}
}
return false;
}
And access it like this:
myString.HasSpecialCharacter();
private bool isMatch(string strValue,string specialChars)
{
return specialChars.Where(x => strValue.Contains(x)).Any();
}
Create a method and call it hasSpecialChar with one parameter
and use foreach to check every single character in the textbox, add as many characters as you want in the array, in my case i just used ) and ( to prevent sql injection .
public void hasSpecialChar(string input)
{
char[] specialChar = {'(',')'};
foreach (char item in specialChar)
{
if (input.Contains(item)) MessageBox.Show("it contains");
}
}
in your button click evenement or you click btn double time like that :
private void button1_Click(object sender, EventArgs e)
{
hasSpecialChar(textbox1.Text);
}
While there are many ways to skin this cat, I prefer to wrap such code into reusable extension methods that make it trivial to do going forward. When using extension methods, you can also avoid RegEx as it is slower than a direct character check. I like using the extensions in the Extensions.cs NuGet package. It makes this check as simple as:
Add the [https://www.nuget.org/packages/Extensions.cs][1] package to your project.
Add "using Extensions;" to the top of your code.
"smith23#".IsAlphaNumeric() will return False whereas "smith23".IsAlphaNumeric() will return True. By default the .IsAlphaNumeric() method ignores spaces, but it can also be overridden such that "smith 23".IsAlphaNumeric(false) will return False since the space is not considered part of the alphabet.
Every other check in the rest of the code is simply MyString.IsAlphaNumeric().
Based on #prmph's answer, it can be even more simplified (omitting the variable, using overload resolution):
yourString.Any(char.IsLetterOrDigit);
No special characters or empty string except hyphen
^[a-zA-Z0-9-]+$