Regex to validate a semi-colon seperated list of emails? - c#

I have the following c# extension method for validating an email address. The regex came from Microsoft on their How to: Verify that Strings Are in Valid Email Format page.
I need to improve this method to be able to handle a semi-colon seperated list of emails. An valid example string could be as badly formatted as: ";; ; ; xxx.sss.xxx ; ;; xxx.sss.xxx;"
/// <summary>
/// Validates the string is an Email Address...
/// </summary>
/// <param name="emailAddress"></param>
/// <returns>bool</returns>
public static bool IsValidEmailAddress(this string emailAddress)
{
var valid = true;
var isnotblank = false;
var email = emailAddress.Trim();
if (email.Length > 0)
{
isnotblank = true;
valid = Regex.IsMatch(email, #"^(?("")("".+?(?<!\\)""#)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])#))" +
#"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-\w]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$", RegexOptions.IgnoreCase);
}
return (valid && isnotblank);
}

Microsoft's regex does a pretty good job. However, it doesn't catch a few strange scenarios and a number of special characters which are valid for email. I'll give you a different regex. Choose to use it or not is your prerogative.
I would separate the concerns by having one extension method which validates an email address and another which validates the list. Do a .trim() on each email before passing it to the email validation method. So, something like this:
/// <summary>
/// Validates the string is an Email Address...
/// </summary>
/// <param name="emailAddress"></param>
/// <returns>bool</returns>
public static bool IsValidEmailAddress(this string emailAddress)
{
var valid = true;
var isnotblank = false;
var email = emailAddress.Trim();
if (email.Length > 0)
{
// Email Address Cannot start with period.
// Name portion must be at least one character
// In the Name, valid characters are: a-z 0-9 ! # _ % & ' " = ` { } ~ - + * ? ^ | / $
// Cannot have period immediately before # sign.
// Cannot have two # symbols
// In the domain, valid characters are: a-z 0-9 - .
// Domain cannot start with a period or dash
// Domain name must be 2 characters.. not more than 256 characters
// Domain cannot end with a period or dash.
// Domain must contain a period
isnotblank = true;
valid = Regex.IsMatch(email, #"\A([\w!#%&'""=`{}~\.\-\+\*\?\^\|\/\$])+#{1}\w+([-.]\w+)*\.\w+([-.]\w+)*\z", RegexOptions.IgnoreCase) &&
!email.StartsWith("-") &&
!email.StartsWith(".") &&
!email.EndsWith(".") &&
!email.Contains("..") &&
!email.Contains(".#") &&
!email.Contains("#.");
}
return (valid && isnotblank);
}
/// <summary>
/// Validates the string is an Email Address or a delimited string of email addresses...
/// </summary>
/// <param name="emailAddress"></param>
/// <returns>bool</returns>
public static bool IsValidEmailAddressDelimitedList(this string emailAddress, char delimiter = ';')
{
var valid = true;
var isnotblank = false;
string[] emails = emailAddress.Split(delimiter);
foreach (string e in emails)
{
var email = e.Trim();
if (email.Length > 0 && valid) // if valid == false, no reason to continue checking
{
isnotblank = true;
if (!email.IsValidEmailAddress())
{
valid = false;
}
}
}
return (valid && isnotblank);
}

You can use the following regex to allow several emails separated by semicolon and (optionally) spaces besides the semicolon. It will also accept single email address that does not end in semicolon.
Right now it allows empty entries as well(no email addresses). If You wanna change it to have at least 1, then replace the final * by + to require at least one address.
(([a-zA-Z0-9_\-\.]+)#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)(\s*;\s*|\s*$))*
If you want to allow comma as well as semicolon, you can chage following :
(\s*;\s*|\s*$)
to this:
(\s*(;|,)\s*|\s*$)

Related

To check if an element is present in a string array using "Regex" in c#

Help me guys to complete the code using Regex. I'm new to learn c#.
Create a C# program to complete the following task using Regular Expressions
i. Create a string variable 'Myinput' and read the value from user
ii. Check whether the given values are matching with the following
Cappuccino
Tea
Milk
Espresso
iii. If any other values except the above, the program need to throw an error
message
using System;
using System.Linq;
using System.Text.RegularExpressions;
namespace RegularExpression
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Enter the user choice input");
string Myinput = Console.ReadLine();
string[] arrayy = new string[4] { "Cappucino", "Tea", "Milk", "Espresso" };
MyChoice obj = new MyChoice();
bool ans = obj.MyChoiceContains(Myinput, arrayy);
Console.WriteLine(ans);
Console.ReadKey();
/// i. Create a string variable 'Myinput' and read the value from user.
/// ii. Check whether the given values are matching with the following
/// 1.Cappuccino 2. Tea 3. Milk 4. Espresso.
/// iii. If any other values except the above, the program need to throw the error
///message.
/// Display valid or invalid message here.
}
}
public class MyChoice
{
public bool MyChoiceContains(string Userchoice, string[] myChoiceValues)
{
Userchoice = #"^[A-Za-z]$";
Regex rg = new Regex(Userchoice);
bool ans = true;
foreach (string s in myChoiceValues)
{
ans = Regex.IsMatch(Userchoice, s);
}
return ans;
}
}
}
/// <summary>
/// Create Class with Class name 'MyChoice'.
/// </summary>
/// <summary>
/// Create a method with the name 'MyChoiceContains'.
/// <param name="Userchoice"></param><type>string</type>
/// <param name="myChoiceValues"></param><type>string[]</type>
/// <returns>bool</returns>return "true" if valid else return "false".
/// Note: Please dont use Console.WriteLine() and Console.ReadLine() in this method.
/// </summary>
In the comments you have stated that the user will input a regular expression, and that you want to check if the regular expression matches on any of the elements in the array. You can either do this manually:
foreach (string s in myChoiceValues)
{
if (rg.IsMatch(s))
return true;
}
return false;
I've moved the return false; outside so that you don't return false; when the first item doesn't match.
Alternatively, you can do this using the LINQ extension methods. You'll need to ensure you have using System.Linq; at the top of your file.
return myChoiceValues.Any(v => rg.IsMatch(v));
// or simply: return myChoiceValues.Any(rg.IsMatch);
You might also need to rewrite your hardcoded test expression (^[A-Za-z]$) as this currently requires a single character between the start and end of the string (i.e. the matched string should be a single character). Perhaps you meant:
^[A-Za-z]+$
The + means it will match 1 or more characters matching from the [A-Za-z] set.
string pattern = #"(Cappuccino | Tea | Milk | Espresso)";
string input = "text";
Match m = Regex.Match(input, pattern, RegexOptions.IgnoreCase);
if (m.Success)
{
// matched
}
else
{
// not matched
}
Ressource

handle escape (#) character in xamarin xaml [duplicate]

I use this
#"^([\w\.\-]+)#([\w\-]+)((\.(\w){2,3})+)$"
regexp to validate the email
([\w\.\-]+) - this is for the first-level domain (many letters and numbers, also point and hyphen)
([\w\-]+) - this is for second-level domain
((\.(\w){2,3})+) - and this is for other level domains(from 3 to infinity) which includes a point and 2 or 3 literals
what's wrong with this regex?
EDIT:it doesn't match the "something#someth.ing" email
TLD's like .museum aren't matched this way, and there are a few other long TLD's. Also, you can validate email addresses using the MailAddress class as Microsoft explains here in a note:
Instead of using a regular expression to validate an email address,
you can use the System.Net.Mail.MailAddress class. To determine
whether an email address is valid, pass the email address to the
MailAddress.MailAddress(String) class constructor.
public bool IsValid(string emailaddress)
{
try
{
MailAddress m = new MailAddress(emailaddress);
return true;
}
catch (FormatException)
{
return false;
}
}
This saves you a lot af headaches because you don't have to write (or try to understand someone else's) regex.
EDIT: For those who are allergic to try/catch: In .NET 5 you can use MailAddress.TryCreate. See also https://stackoverflow.com/a/68198658, including an example how to fix .., spaces, missing .TLD, etc.
I think #"^([\w\.\-]+)#([\w\-]+)((\.(\w){2,3})+)$" should work.
You need to write it like
string email = txtemail.Text;
Regex regex = new Regex(#"^([\w\.\-]+)#([\w\-]+)((\.(\w){2,3})+)$");
Match match = regex.Match(email);
if (match.Success)
Response.Write(email + " is correct");
else
Response.Write(email + " is incorrect");
Be warned that this will fail if:
There is a subdomain after the # symbol.
You use a TLD with a length greater than 3, such as .info
I have an expression for checking email addresses that I use.
Since none of the above were as short or as accurate as mine, I thought I would post it here.
#"^[\w!#$%&'*+\-/=?\^_`{|}~]+(\.[\w!#$%&'*+\-/=?\^_`{|}~]+)*"
+ "#"
+ #"((([\-\w]+\.)+[a-zA-Z]{2,4})|(([0-9]{1,3}\.){3}[0-9]{1,3}))$";
For more info go read about it here: C# – Email Regular Expression
Also, this checks for RFC validity based on email syntax, not for whether the email really exists. The only way to test that an email really exists is to send and email and have the user verify they received the email by clicking a link or entering a token.
Then there are throw-away domains, such as Mailinator.com, and such. This doesn't do anything to verify whether an email is from a throwaway domain or not.
I found nice document on MSDN for it.
How to: Verify that Strings Are in Valid Email Format
http://msdn.microsoft.com/en-us/library/01escwtf.aspx
(check out that this code also supports the use of non-ASCII characters for Internet domain names.)
There are 2 implementation, for .Net 2.0/3.0 and for .Net 3.5 and higher.
the 2.0/3.0 version is:
bool IsValidEmail(string strIn)
{
// Return true if strIn is in valid e-mail format.
return Regex.IsMatch(strIn, #"^([\w-\.]+)#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$");
}
My tests over this method give:
Invalid: #majjf.com
Invalid: A#b#c#example.com
Invalid: Abc.example.com
Valid: j..s#proseware.com
Valid: j.#server1.proseware.com
Invalid: js*#proseware.com
Invalid: js#proseware..com
Valid: ma...ma#jjf.co
Valid: ma.#jjf.com
Invalid: ma##jjf.com
Invalid: ma#jjf.
Invalid: ma#jjf..com
Invalid: ma#jjf.c
Invalid: ma_#jjf
Invalid: ma_#jjf.
Valid: ma_#jjf.com
Invalid: -------
Valid: 12#hostname.com
Valid: d.j#server1.proseware.com
Valid: david.jones#proseware.com
Valid: j.s#server1.proseware.com
Invalid: j#proseware.com9
Valid: j_9#[129.126.118.1]
Valid: jones#ms1.proseware.com
Invalid: js#internal#proseware.com
Invalid: js#proseware.com9
Invalid: js#proseware.com9
Valid: m.a#hostname.co
Valid: m_a1a#hostname.com
Valid: ma.h.saraf.onemore#hostname.com.edu
Valid: ma#hostname.com
Invalid: ma#hostname.comcom
Invalid: MA#hostname.coMCom
Valid: ma12#hostname.com
Valid: ma-a.aa#hostname.com.edu
Valid: ma-a#hostname.com
Valid: ma-a#hostname.com.edu
Valid: ma-a#1hostname.com
Valid: ma.a#1hostname.com
Valid: ma#1hostname.com
The following code is based on Microsoft's Data annotations implementation on github and I think it's the most complete validation for emails:
public static Regex EmailValidation()
{
const string pattern = #"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))#((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$";
const RegexOptions options = RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture;
// Set explicit regex match timeout, sufficient enough for email parsing
// Unless the global REGEX_DEFAULT_MATCH_TIMEOUT is already set
TimeSpan matchTimeout = TimeSpan.FromSeconds(2);
try
{
if (AppDomain.CurrentDomain.GetData("REGEX_DEFAULT_MATCH_TIMEOUT") == null)
{
return new Regex(pattern, options, matchTimeout);
}
}
catch
{
// Fallback on error
}
// Legacy fallback (without explicit match timeout)
return new Regex(pattern, options);
}
This does not meet all of the requirements of RFCs 5321 and 5322, but it works with the following definitions.
#"^([0-9a-zA-Z]([\+\-_\.][0-9a-zA-Z]+)*)+"#(([0-9a-zA-Z][-\w]*[0-9a-zA-Z]*\.)+[a-zA-Z0-9]{2,17})$";
Below is the code
const String pattern =
#"^([0-9a-zA-Z]" + //Start with a digit or alphabetical
#"([\+\-_\.][0-9a-zA-Z]+)*" + // No continuous or ending +-_. chars in email
#")+" +
#"#(([0-9a-zA-Z][-\w]*[0-9a-zA-Z]*\.)+[a-zA-Z0-9]{2,17})$";
var validEmails = new[] {
"ma#hostname.com",
"ma#hostname.comcom",
"MA#hostname.coMCom",
"m.a#hostname.co",
"m_a1a#hostname.com",
"ma-a#hostname.com",
"ma-a#hostname.com.edu",
"ma-a.aa#hostname.com.edu",
"ma.h.saraf.onemore#hostname.com.edu",
"ma12#hostname.com",
"12#hostname.com",
};
var invalidEmails = new[] {
"Abc.example.com", // No `#`
"A#b#c#example.com", // multiple `#`
"ma...ma#jjf.co", // continuous multiple dots in name
"ma#jjf.c", // only 1 char in extension
"ma#jjf..com", // continuous multiple dots in domain
"ma##jjf.com", // continuous multiple `#`
"#majjf.com", // nothing before `#`
"ma.#jjf.com", // nothing after `.`
"ma_#jjf.com", // nothing after `_`
"ma_#jjf", // no domain extension
"ma_#jjf.", // nothing after `_` and .
"ma#jjf.", // nothing after `.`
};
foreach (var str in validEmails)
{
Console.WriteLine("{0} - {1} ", str, Regex.IsMatch(str, pattern));
}
foreach (var str in invalidEmails)
{
Console.WriteLine("{0} - {1} ", str, Regex.IsMatch(str, pattern));
}
Best email validation regex
[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?
And it's usage :-
bool isEmail = Regex.IsMatch(emailString, #"\A(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\Z", RegexOptions.IgnoreCase);
new System.ComponentModel.DataAnnotations.EmailAddressAttribute().IsValid(input)
As an update to the popular answer of Alex: In .NET 5 MailAddress now has a TryCreate. So you can do something like:
public static bool IsValidEmail(string email)
{
if (!MailAddress.TryCreate(email, out var mailAddress))
return false;
// And if you want to be more strict:
var hostParts = mailAddress.Host.Split('.');
if (hostParts.Length == 1)
return false; // No dot.
if (hostParts.Any(p => p == string.Empty))
return false; // Double dot.
if (hostParts[^1].Length < 2)
return false; // TLD only one letter.
if (mailAddress.User.Contains(' '))
return false;
if (mailAddress.User.Split('.').Any(p => p == string.Empty))
return false; // Double dot or dot at end of user part.
return true;
}
Why not use EF6 attribute based e-mail validation?
As you can see above, Regex validation for e-mail always has some hole in it. If you are using EF6 data annotations, you can easily achieve reliable and stronger e-mail validation with EmailAddress data annotation attribute available for that. I had to remove the regex validation I used before for e-mail when I got mobile device specific regex failure on e-mail input field. When the data annotation attribute used for e-mail validation, the issue on mobile was resolved.
public class LoginViewModel
{
[EmailAddress(ErrorMessage = "The email format is not valid")]
public string Email{ get; set; }
Try this on for size:
public static bool IsValidEmailAddress(this string s)
{
var regex = new Regex(#"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?");
return regex.IsMatch(s);
}
This regex works perfectly:
bool IsValidEmail(string email)
{
return Regex.IsMatch(email, #"^[\w!#$%&'*+\-/=?\^_`{|}~]+(\.[\w!#$%&'*+\-/=?\^_`{|}~]+)*#((([\-\w]+\.)+[a-zA-Z]{2,4})|(([0-9]{1,3}\.){3}[0-9]{1,3}))\z");
}
Email validation using regex
string pattern = #"\A(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\Z";
//check first string
if (Regex.IsMatch(EmailId1 , pattern))
{
//if email is valid
Console.WriteLine(EmailId1+ " is a valid Email address ");
}
Source: email validation c#
Validation Without Regex using MailAddress.MailAddress(String) class constructor
public bool IsEmailValid(string emailaddress)
{
try
{
MailAddress m = new MailAddress(emailaddress);
return true;
}
catch (FormatException)
{
return false;
}
}
This one prevents invalid emails mentioned by others in the comments:
Abc.#example.com
Abc..123#example.com
name#hotmail
toms.email.#gmail.com
test#-online.com
It also prevents emails with double dots:
hello..world#example..com
Try testing it with as many invalid email addresses as you can find.
using System.Text.RegularExpressions;
public static bool IsValidEmail(string email)
{
return Regex.IsMatch(email, #"\A[a-z0-9]+([-._][a-z0-9]+)*#([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,4}\z")
&& Regex.IsMatch(email, #"^(?=.{1,64}#.{4,64}$)(?=.{6,100}$).*");
}
See validate email address using regular expression in C#.
Try this, it's working for me:
public bool IsValidEmailAddress(string s)
{
if (string.IsNullOrEmpty(s))
return false;
else
{
var regex = new Regex(#"\w+([-+.']\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*");
return regex.IsMatch(s) && !s.EndsWith(".");
}
}
To validate your email ID, you can simply create such method and use it.
public static bool IsValidEmail(string email)
{
var r = new Regex(#"^([0-9a-zA-Z]([-\.\w]*[0-9a-zA-Z])*#([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$");
return !String.IsNullOrEmpty(email) && r.IsMatch(email);
}
This will return True / False. (Valid / Invalid Email Id)
It has taken many attempts to create an email validator which catches nearly all worldwide requirements for email.
Extension method you can call with:
myEmailString.IsValidEmailAddress();
Regex pattern string you can get by calling:
var myPattern = Regex.EmailPattern;
The Code (mostly comments):
/// <summary>
/// Validates the string is an Email Address...
/// </summary>
/// <param name="emailAddress"></param>
/// <returns>bool</returns>
public static bool IsValidEmailAddress(this string emailAddress)
{
var valid = true;
var isnotblank = false;
var email = emailAddress.Trim();
if (email.Length > 0)
{
// Email Address Cannot start with period.
// Name portion must be at least one character
// In the Name, valid characters are: a-z 0-9 ! # _ % & ' " = ` { } ~ - + * ? ^ | / $
// Cannot have period immediately before # sign.
// Cannot have two # symbols
// In the domain, valid characters are: a-z 0-9 - .
// Domain cannot start with a period or dash
// Domain name must be 2 characters.. not more than 256 characters
// Domain cannot end with a period or dash.
// Domain must contain a period
isnotblank = true;
valid = Regex.IsMatch(email, Regex.EmailPattern, RegexOptions.IgnoreCase) &&
!email.StartsWith("-") &&
!email.StartsWith(".") &&
!email.EndsWith(".") &&
!email.Contains("..") &&
!email.Contains(".#") &&
!email.Contains("#.");
}
return (valid && isnotblank);
}
/// <summary>
/// Validates the string is an Email Address or a delimited string of email addresses...
/// </summary>
/// <param name="emailAddress"></param>
/// <returns>bool</returns>
public static bool IsValidEmailAddressDelimitedList(this string emailAddress, char delimiter = ';')
{
var valid = true;
var isnotblank = false;
string[] emails = emailAddress.Split(delimiter);
foreach (string e in emails)
{
var email = e.Trim();
if (email.Length > 0 && valid) // if valid == false, no reason to continue checking
{
isnotblank = true;
if (!email.IsValidEmailAddress())
{
valid = false;
}
}
}
return (valid && isnotblank);
}
public class Regex
{
/// <summary>
/// Set of Unicode Characters currently supported in the application for email, etc.
/// </summary>
public static readonly string UnicodeCharacters = "À-ÿ\p{L}\p{M}ÀàÂâÆæÇçÈèÉéÊêËëÎîÏïÔôŒœÙùÛûÜü«»€₣äÄöÖüÜß"; // German and French
/// <summary>
/// Set of Symbol Characters currently supported in the application for email, etc.
/// Needed if a client side validator is being used.
/// Not needed if validation is done server side.
/// The difference is due to subtle differences in Regex engines.
/// </summary>
public static readonly string SymbolCharacters = #"!#%&'""=`{}~\.\-\+\*\?\^\|\/\$";
/// <summary>
/// Regular Expression string pattern used to match an email address.
/// The following characters will be supported anywhere in the email address:
/// ÀàÂâÆæÇçÈèÉéÊêËëÎîÏïÔôŒœÙùÛûÜü«»€₣äÄöÖüÜß[a - z][A - Z][0 - 9] _
/// The following symbols will be supported in the first part of the email address(before the # symbol):
/// !#%&'"=`{}~.-+*?^|\/$
/// Emails cannot start or end with periods,dashes or #.
/// Emails cannot have two # symbols.
/// Emails must have an # symbol followed later by a period.
/// Emails cannot have a period before or after the # symbol.
/// </summary>
public static readonly string EmailPattern = String.Format(
#"^([\w{0}{2}])+#{1}[\w{0}]+([-.][\w{0}]+)*\.[\w{0}]+([-.][\w{0}]+)*$", // #"^[{0}\w]+([-+.'][{0}\w]+)*#[{0}\w]+([-.][{0}\w]+)*\.[{0}\w]+([-.][{0}\w]+)*$",
UnicodeCharacters,
"{1}",
SymbolCharacters
);
}
public static bool ValidateEmail(string str)
{
return Regex.IsMatch(str, #"\w+([-+.']\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*");
}
I use the above code to validate the email address.
public bool VailidateEntriesForAccount()
{
if (!(txtMailId.Text.Trim() == string.Empty))
{
if (!IsEmail(txtMailId.Text))
{
Logger.Debug("Entered invalid Email ID's");
MessageBox.Show("Please enter valid Email Id's" );
txtMailId.Focus();
return false;
}
}
}
private bool IsEmail(string strEmail)
{
Regex validateEmail = new Regex("^[\\W]*([\\w+\\-.%]+#[\\w\\-.]+\\.[A-Za-z] {2,4}[\\W]*,{1}[\\W]*)*([\\w+\\-.%]+#[\\w\\-.]+\\.[A-Za-z]{2,4})[\\W]*$");
return validateEmail.IsMatch(strEmail);
}
This is my favorite approach to this so far:
public static class CommonExtensions
{
public static bool IsValidEmail(this string thisEmail)
=> !string.IsNullOrWhiteSpace(thisEmail) &&
new Regex(#"^([\w\.\-]+)#([\w\-]+)((\.(\w){2,3})+)$").IsMatch(thisEmail);
}
Then use the created string extension like:
if (!emailAsString.IsValidEmail()) throw new Exception("Invalid Email");
There's no perfect regular expression, but this one is pretty strong, I think, based on study of RFC5322. And with C# string interpolation, pretty easy to follow, I think, as well.
const string atext = #"a-zA-Z\d!#\$%&'\*\+-/=\?\^_`\{\|\}~";
var localPart = $"[{atext}]+(\\.[{atext}]+)*";
var domain = $"[{atext}]+(\\.[{atext}]+)*";
Assert.That(() => EmailRegex = new Regex($"^{localPart}#{domain}$", Compiled),
Throws.Nothing);
Vetted with NUnit 2.x.
Just let me know IF it doesn't work :)
public static bool isValidEmail(this string email)
{
string[] mail = email.Split(new string[] { "#" }, StringSplitOptions.None);
if (mail.Length != 2)
return false;
//check part before ...#
if (mail[0].Length < 1)
return false;
System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex(#"^[a-zA-Z0-9_\-\.]+$");
if (!regex.IsMatch(mail[0]))
return false;
//check part after #...
string[] domain = mail[1].Split(new string[] { "." }, StringSplitOptions.None);
if (domain.Length < 2)
return false;
regex = new System.Text.RegularExpressions.Regex(#"^[a-zA-Z0-9_\-]+$");
foreach (string d in domain)
{
if (!regex.IsMatch(d))
return false;
}
//get TLD
if (domain[domain.Length - 1].Length < 2)
return false;
return true;
}
I've created a FormValidationUtils class to validate email:
public static class FormValidationUtils
{
const string ValidEmailAddressPattern = "^[A-Z0-9._%+-]+#[A-Z0-9.-]+\\.[A-Z]{2,6}$";
public static bool IsEmailValid(string email)
{
var regex = new Regex(ValidEmailAddressPattern, RegexOptions.IgnoreCase);
return regex.IsMatch(email);
}
}
Try the Following Code:
using System.Text.RegularExpressions;
if (!Regex.IsMatch(txtEmail.Text, #"^[a-z,A-Z]{1,10}((-|.)\w+)*#\w+.\w{3}$"))
MessageBox.Show("Not valid email.");
STRING SEARCH USING REGEX METHOD IN C#
How to validate an Email by Regular Expression?
string EmailPattern = #"\w+([-+.']\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*";
if (Regex.IsMatch(Email, EmailPattern, RegexOptions.IgnoreCase))
{
Console.WriteLine("Email: {0} is valid.", Email);
}
else
{
Console.WriteLine("Email: {0} is not valid.", Email);
}
Use Reference String.Regex() Method
string patternEmail = #"(?<email>\w+#\w+\.[a-z]{0,3})";
Regex regexEmail = new Regex(patternEmail);
1
^[\w!#$%&'*+\-/=?\^_`{|}~]+(\.[\w!#$%&'*+\-/=?\^_`{|}~]+)*#((([\-\w]+\.)+[a-zA-Z]{2,4})|(([0-9]{1,3}\.){3}[0-9]{1,3}))$
2
^(([^<>()[\]\\.,;:\s#\""]+(\.[^<>()[\]\\.,;:\s#\""]+)*)|(\"".+\""))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$
I think your caret and dollar sign are part of the problem
You should also modify the regex a little, I use the next
#"[ :]+([\w.-]+)#([\w-.])+((.(\w){2,3})+)"
Regex Email Pattern:
^(?:[\\w\\!\\#\\$\\%\\&\\'\\*\\+\\-\\/\\=\\?\\^\\`\\{\\|\\}\\~]+\\.)*[\\w\\!\\#\\$\\%\\&\\'\\*\\+\\-\\/\\=\\?\\^\\`\\{\\|\\}\\~]+#(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\\-](?!\\.)){0,61}[a-zA-Z0-9]?\\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\\[(?:(?:[01]?\\d{1,2}|2[0-4]\\d|25[0-5])\\.){3}(?:[01]?\\d{1,2}|2[0-4]\\d|25[0-5])\\]))$
I've been using the Regex.IsMatch().
First of all you need to add the next statement:
using System.Text.RegularExpressions;
Then the method looks like:
private bool EmailValidation(string pEmail)
{
return Regex.IsMatch(pEmail,
#"^(?("")("".+?(?<!\\)""#)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])#))" +
#"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-\w]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$",
RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250));
}
It's a private method because of my logic but you can put the method as static in another Layer such as "Utilities" and call it from where you need.

C# Read Multiple Tags in a string

I want to allow user so they can read more than one tag in a string. So far, user could only add one tag
if (rtb.Text.Contains("[b]"))
{
Regex regex = new Regex(#"\[b\](.*)\[/b\]");
var v = regex.Match(rtb.Text);
string s = v.Groups[1].ToString();
rtb.SelectionStart = rtb.Text.IndexOf("[b]");
rtb.SelectionLength = s.Length + 7;
rtb.SelectionFont = new Font(rtb.Font.FontFamily, rtb.Font.Size, FontStyle.Bold);
rtb.SelectedText = s;
}
else if (rtb.Text.Contains("[i]"))
{
Regex regex = new Regex(#"\[i\](.*)\[/i\]");
var v = regex.Match(rtb.Text);
string s = v.Groups[1].ToString();
rtb.SelectionStart = rtb.Text.IndexOf("[b]");
rtb.SelectionLength = s.Length + 7;
rtb.SelectionFont = new Font(rtb.Font.FontFamily, rtb.Font.Size, FontStyle.Italic);
rtb.SelectedText = s;
}
richTextBox1.Select(richTextBox1.TextLength, 0);
richTextBox1.SelectedRtf = rtb.Rtf;
If i have this string:
"Hello [b]World[/b] Meet the [b]Programmer[/b]"
the output would be like this:
"Hello World Meet the Programmer"
And if i have this string:
"Hello [b]World[/b] Meet the [i]Programmer[/i]"
the output would be like this:
"Hello World Meet the [i]Programmer[/i]"
How to read multiple tags from a string? like, in a string if i have 2 [b][/b] tags, 5 [i][/i] tags or even mixed tags ([b][i][/i][/b])?
Two problems:
1. Greedy matching semantics of Regex
\[b\](.*)\[/b\] looks for the longest possible match within your string, i.e. it is greedy. In your example, you expect it to match [b]World[/b], when in fact it matches [b]World[/b] Meet the [b]Programmer[/b] (consequently making "Meet the" bold as well). This can easily be resolved using non-greedy syntax: \[b\](.*?)\[/b\] (note the extra ?)
Details: How to Match with Regex "shortest match" in .NET
2. You are only looking for one occurrence of tags!
Obviously, your code will only highlight a single [b]/[i] tag. Don't use else if if you want [i] to be handled if your string contains [b]. Use loops and Regex.Matches if you want to handle all occurrences of your regular expression instead of just the first one.
Without Regex but still must be adapted slightly.
The test:
[Test]
public void Text()
{
string str = "[b]Hello[/b] This is sample text [b] Goodbye [/b]";
var bold = AllIndexesOf(str, "b").ToArray();
// Assume the IEnumerable is even else it should of thrown an error
for (int i = 0; i < bold.Count(); i += 2)
{
Console.WriteLine($"Pair: {bold[i]} | {bold[i+1]}");
}
// str.AllIndexesOf
}
Here is the method.
/// <summary>
/// Courtesy of : http://stackoverflow.com/a/24016130/5282506
/// Adapted by me.
///
/// Pass in the unique symbol and itll find the first and last index pairs
/// Can adapt to find all unique pairs at once.
/// </summary>
/// <param name="str">The string.</param>
/// <param name="searchstring">The searchstring letter (b, i, etc)</param>
/// <returns></returns>
public static IEnumerable<int> AllIndexesOf(string str, string searchstring)
{
//assumes the string is formatted correctly. Only one tag of the same type inside each tag.
int minIndex = str.IndexOf("["+searchstring+"]");
while (minIndex != -1)
{
Console.WriteLine("First: {0}", minIndex);
yield return minIndex;
var maxIndexEnd = str.IndexOf("[/"+ searchstring +"]", minIndex + searchstring.Length +3);//added three for the [/ and ] characters.
Console.WriteLine("End: {0}", maxIndexEnd);
if (maxIndexEnd == -1)
{
//Malformed string, no end element for a found start element
//Do something...
throw new FormatException("Malformed string");
}
yield return maxIndexEnd;
minIndex = str.IndexOf("[" + searchstring+"]", maxIndexEnd + searchstring.Length+2);//added two for the [ and ] characters
}
}
If you wish to make it an extension method for string change signature to this:
public static IEnumerable<int> AllIndexesOf(this string str, string searchstring)
Heres the console result for bold indexes:
Pair: 0 | 8
Pair: 33 | 45
I have not fully tested this method for all edge cases.

How to get the user initials programmatically in Windows Forms

I'm able to get this folder:
C:\Users\XXX\Documents
out of my code:
string myDocuments =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
I need to get the userinitials (XXX), is there nice way of getting it, like MyDocuments?
If so I would prefer that clean way, instead of chooping the username out of my folder-path.
You may checkout the UserName property:
string username = Environment.UserName;
use Environment.UserName to get the user name of the person who is currently logged on to the Windows operating system.
Office application ask & store initials, but not windows
So, as other post, use Environment.UserName
Then, use this extension method to retrieve initials (it take first letter of each words)
/// <summary>
/// Remove any special character and return the first letters of all words
/// </summary>
/// <param name="str">String to retrieve the initials from</param>
/// <returns></returns>
public static string GetInitials(this string str)
{
if (string.IsNullOrEmpty(str))
return str;
var space = ' ';
var previousWasSpace = true;
var buffer = new char[str.Length];
var index = 0;
foreach (char c in str)
{
if (c == space)
{
previousWasSpace = true;
}
else if (previousWasSpace && char.IsLetterOrDigit(c))
{
previousWasSpace = false;
buffer[index] = c;
index++;
}
}
return new string(buffer, 0, index);
}
This method is old and may be optimized
Here's another way to get the initials from username (splits by spaces or periods in this example):
foreach (string s in Environment.UserName.Split(" .".ToCharArray()))
userInitials += s.Substring(0,1).ToUpper();

Named string formatting in C#

Is there any way to format a string by name rather than position in C#?
In python, I can do something like this example (shamelessly stolen from here):
>>> print '%(language)s has %(#)03d quote types.' % \
{'language': "Python", "#": 2}
Python has 002 quote types.
Is there any way to do this in C#? Say for instance:
String.Format("{some_variable}: {some_other_variable}", ...);
Being able to do this using a variable name would be nice, but a dictionary is acceptable too.
There is no built-in method for handling this.
Here's one method
string myString = "{foo} is {bar} and {yadi} is {yada}".Inject(o);
Here's another
Status.Text = "{UserName} last logged in at {LastLoginDate}".FormatWith(user);
A third improved method partially based on the two above, from Phil Haack
Update: This is now built-in as of C# 6 (released in 2015).
String Interpolation
$"{some_variable}: {some_other_variable}"
I have an implementation I just posted to my blog here: http://haacked.com/archive/2009/01/04/fun-with-named-formats-string-parsing-and-edge-cases.aspx
It addresses some issues that these other implementations have with brace escaping. The post has details. It does the DataBinder.Eval thing too, but is still very fast.
Interpolated strings were added into C# 6.0 and Visual Basic 14
Both were introduced through new Roslyn compiler in Visual Studio 2015.
C# 6.0:
return "\{someVariable} and also \{someOtherVariable}" OR
return $"{someVariable} and also {someOtherVariable}"
source: what's new in C#6.0
 
VB 14:
return $"{someVariable} and also {someOtherVariable}"
source: what's new in VB 14
Noteworthy features (in Visual Studio 2015 IDE):
syntax coloring is supported - variables contained in strings are highlighted
refactoring is supported - when renaming, variables contained in strings get renamed, too
actually not only variable names, but expressions are supported - e.g. not only {index} works, but also {(index + 1).ToString().Trim()}
Enjoy! (& click "Send a Smile" in the VS)
You can also use anonymous types like this:
public string Format(string input, object p)
{
foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(p))
input = input.Replace("{" + prop.Name + "}", (prop.GetValue(p) ?? "(null)").ToString());
return input;
}
Of course it would require more code if you also want to parse formatting, but you can format a string using this function like:
Format("test {first} and {another}", new { first = "something", another = "something else" })
There doesn't appear to be a way to do this out of the box. Though, it looks feasible to implement your own IFormatProvider that links to an IDictionary for values.
var Stuff = new Dictionary<string, object> {
{ "language", "Python" },
{ "#", 2 }
};
var Formatter = new DictionaryFormatProvider();
// Interpret {0:x} where {0}=IDictionary and "x" is hash key
Console.WriteLine string.Format(Formatter, "{0:language} has {0:#} quote types", Stuff);
Outputs:
Python has 2 quote types
The caveat is that you can't mix FormatProviders, so the fancy text formatting can't be used at the same time.
The framework itself does not provide a way to do this, but you can take a look at this post by Scott Hanselman. Example usage:
Person p = new Person();
string foo = p.ToString("{Money:C} {LastName}, {ScottName} {BirthDate}");
Assert.AreEqual("$3.43 Hanselman, {ScottName} 1/22/1974 12:00:00 AM", foo);
This code by James Newton-King is similar and works with sub-properties and indexes,
string foo = "Top result for {Name} was {Results[0].Name}".FormatWith(student));
James's code relies on System.Web.UI.DataBinder to parse the string and requires referencing System.Web, which some people don't like to do in non-web applications.
EDIT: Oh and they work nicely with anonymous types, if you don't have an object with properties ready for it:
string name = ...;
DateTime date = ...;
string foo = "{Name} - {Birthday}".FormatWith(new { Name = name, Birthday = date });
See https://stackoverflow.com/questions/271398?page=2#358259
With the linked-to extension you can write this:
var str = "{foo} {bar} {baz}".Format(foo=>"foo", bar=>2, baz=>new object());
and you'll get "foo 2 System.Object".
I think the closest you'll get is an indexed format:
String.Format("{0} has {1} quote types.", "C#", "1");
There's also String.Replace(), if you're willing to do it in multiple steps and take it on faith that you won't find your 'variables' anywhere else in the string:
string MyString = "{language} has {n} quote types.";
MyString = MyString.Replace("{language}", "C#").Replace("{n}", "1");
Expanding this to use a List:
List<KeyValuePair<string, string>> replacements = GetFormatDictionary();
foreach (KeyValuePair<string, string> item in replacements)
{
MyString = MyString.Replace(item.Key, item.Value);
}
You could do that with a Dictionary<string, string> too by iterating it's .Keys collections, but by using a List<KeyValuePair<string, string>> we can take advantage of the List's .ForEach() method and condense it back to a one-liner:
replacements.ForEach(delegate(KeyValuePair<string,string>) item) { MyString = MyString.Replace(item.Key, item.Value);});
A lambda would be even simpler, but I'm still on .Net 2.0. Also note that the .Replace() performance isn't stellar when used iteratively, since strings in .Net are immutable. Also, this requires the MyString variable be defined in such a way that it's accessible to the delegate, so it's not perfect yet.
My open source library, Regextra, supports named formatting (amongst other things). It currently targets .NET 4.0+ and is available on NuGet. I also have an introductory blog post about it: Regextra: helping you reduce your (problems){2}.
The named formatting bit supports:
Basic formatting
Nested properties formatting
Dictionary formatting
Escaping of delimiters
Standard/Custom/IFormatProvider string formatting
Example:
var order = new
{
Description = "Widget",
OrderDate = DateTime.Now,
Details = new
{
UnitPrice = 1500
}
};
string template = "We just shipped your order of '{Description}', placed on {OrderDate:d}. Your {{credit}} card will be billed {Details.UnitPrice:C}.";
string result = Template.Format(template, order);
// or use the extension: template.FormatTemplate(order);
Result:
We just shipped your order of 'Widget', placed on 2/28/2014. Your {credit} card will be billed $1,500.00.
Check out the project's GitHub link (above) and wiki for other examples.
private static Regex s_NamedFormatRegex = new Regex(#"\{(?!\{)(?<key>[\w]+)(:(?<fmt>(\{\{|\}\}|[^\{\}])*)?)?\}", RegexOptions.Compiled);
public static StringBuilder AppendNamedFormat(this StringBuilder builder,IFormatProvider provider, string format, IDictionary<string, object> args)
{
if (builder == null) throw new ArgumentNullException("builder");
var str = s_NamedFormatRegex.Replace(format, (mt) => {
string key = mt.Groups["key"].Value;
string fmt = mt.Groups["fmt"].Value;
object value = null;
if (args.TryGetValue(key,out value)) {
return string.Format(provider, "{0:" + fmt + "}", value);
} else {
return mt.Value;
}
});
builder.Append(str);
return builder;
}
public static StringBuilder AppendNamedFormat(this StringBuilder builder, string format, IDictionary<string, object> args)
{
if (builder == null) throw new ArgumentNullException("builder");
return builder.AppendNamedFormat(null, format, args);
}
Example:
var builder = new StringBuilder();
builder.AppendNamedFormat(
#"你好,{Name},今天是{Date:yyyy/MM/dd}, 这是你第{LoginTimes}次登录,积分{Score:{{ 0.00 }}}",
new Dictionary<string, object>() {
{ "Name", "wayjet" },
{ "LoginTimes",18 },
{ "Score", 100.4 },
{ "Date",DateTime.Now }
});
Output:
你好,wayjet,今天是2011-05-04, 这是你第18次登录,积分{ 100.40 }
Check this one:
public static string StringFormat(string format, object source)
{
var matches = Regex.Matches(format, #"\{(.+?)\}");
List<string> keys = (from Match matche in matches select matche.Groups[1].Value).ToList();
return keys.Aggregate(
format,
(current, key) =>
{
int colonIndex = key.IndexOf(':');
return current.Replace(
"{" + key + "}",
colonIndex > 0
? DataBinder.Eval(source, key.Substring(0, colonIndex), "{0:" + key.Substring(colonIndex + 1) + "}")
: DataBinder.Eval(source, key).ToString());
});
}
Sample:
string format = "{foo} is a {bar} is a {baz} is a {qux:#.#} is a really big {fizzle}";
var o = new { foo = 123, bar = true, baz = "this is a test", qux = 123.45, fizzle = DateTime.Now };
Console.WriteLine(StringFormat(format, o));
Performance is pretty ok compared to other solutions.
I doubt this will be possible. The first thing that comes to mind is how are you going to get access to local variable names?
There might be some clever way using LINQ and Lambda expressions to do this however.
Here's one I made a while back. It extends String with a Format method taking a single argument. The nice thing is that it'll use the standard string.Format if you provide a simple argument like an int, but if you use something like anonymous type it'll work too.
Example usage:
"The {Name} family has {Children} children".Format(new { Children = 4, Name = "Smith" })
Would result in "The Smith family has 4 children."
It doesn't do crazy binding stuff like arrays and indexers. But it is super simple and high performance.
public static class AdvancedFormatString
{
/// <summary>
/// An advanced version of string.Format. If you pass a primitive object (string, int, etc), it acts like the regular string.Format. If you pass an anonmymous type, you can name the paramters by property name.
/// </summary>
/// <param name="formatString"></param>
/// <param name="arg"></param>
/// <returns></returns>
/// <example>
/// "The {Name} family has {Children} children".Format(new { Children = 4, Name = "Smith" })
///
/// results in
/// "This Smith family has 4 children
/// </example>
public static string Format(this string formatString, object arg, IFormatProvider format = null)
{
if (arg == null)
return formatString;
var type = arg.GetType();
if (Type.GetTypeCode(type) != TypeCode.Object || type.IsPrimitive)
return string.Format(format, formatString, arg);
var properties = TypeDescriptor.GetProperties(arg);
return formatString.Format((property) =>
{
var value = properties[property].GetValue(arg);
return Convert.ToString(value, format);
});
}
public static string Format(this string formatString, Func<string, string> formatFragmentHandler)
{
if (string.IsNullOrEmpty(formatString))
return formatString;
Fragment[] fragments = GetParsedFragments(formatString);
if (fragments == null || fragments.Length == 0)
return formatString;
return string.Join(string.Empty, fragments.Select(fragment =>
{
if (fragment.Type == FragmentType.Literal)
return fragment.Value;
else
return formatFragmentHandler(fragment.Value);
}).ToArray());
}
private static Fragment[] GetParsedFragments(string formatString)
{
Fragment[] fragments;
if ( parsedStrings.TryGetValue(formatString, out fragments) )
{
return fragments;
}
lock (parsedStringsLock)
{
if ( !parsedStrings.TryGetValue(formatString, out fragments) )
{
fragments = Parse(formatString);
parsedStrings.Add(formatString, fragments);
}
}
return fragments;
}
private static Object parsedStringsLock = new Object();
private static Dictionary<string,Fragment[]> parsedStrings = new Dictionary<string,Fragment[]>(StringComparer.Ordinal);
const char OpeningDelimiter = '{';
const char ClosingDelimiter = '}';
/// <summary>
/// Parses the given format string into a list of fragments.
/// </summary>
/// <param name="format"></param>
/// <returns></returns>
static Fragment[] Parse(string format)
{
int lastCharIndex = format.Length - 1;
int currFragEndIndex;
Fragment currFrag = ParseFragment(format, 0, out currFragEndIndex);
if (currFragEndIndex == lastCharIndex)
{
return new Fragment[] { currFrag };
}
List<Fragment> fragments = new List<Fragment>();
while (true)
{
fragments.Add(currFrag);
if (currFragEndIndex == lastCharIndex)
{
break;
}
currFrag = ParseFragment(format, currFragEndIndex + 1, out currFragEndIndex);
}
return fragments.ToArray();
}
/// <summary>
/// Finds the next delimiter from the starting index.
/// </summary>
static Fragment ParseFragment(string format, int startIndex, out int fragmentEndIndex)
{
bool foundEscapedDelimiter = false;
FragmentType type = FragmentType.Literal;
int numChars = format.Length;
for (int i = startIndex; i < numChars; i++)
{
char currChar = format[i];
bool isOpenBrace = currChar == OpeningDelimiter;
bool isCloseBrace = isOpenBrace ? false : currChar == ClosingDelimiter;
if (!isOpenBrace && !isCloseBrace)
{
continue;
}
else if (i < (numChars - 1) && format[i + 1] == currChar)
{//{{ or }}
i++;
foundEscapedDelimiter = true;
}
else if (isOpenBrace)
{
if (i == startIndex)
{
type = FragmentType.FormatItem;
}
else
{
if (type == FragmentType.FormatItem)
throw new FormatException("Two consequtive unescaped { format item openers were found. Either close the first or escape any literals with another {.");
//curr character is the opening of a new format item. so we close this literal out
string literal = format.Substring(startIndex, i - startIndex);
if (foundEscapedDelimiter)
literal = ReplaceEscapes(literal);
fragmentEndIndex = i - 1;
return new Fragment(FragmentType.Literal, literal);
}
}
else
{//close bracket
if (i == startIndex || type == FragmentType.Literal)
throw new FormatException("A } closing brace existed without an opening { brace.");
string formatItem = format.Substring(startIndex + 1, i - startIndex - 1);
if (foundEscapedDelimiter)
formatItem = ReplaceEscapes(formatItem);//a format item with a { or } in its name is crazy but it could be done
fragmentEndIndex = i;
return new Fragment(FragmentType.FormatItem, formatItem);
}
}
if (type == FragmentType.FormatItem)
throw new FormatException("A format item was opened with { but was never closed.");
fragmentEndIndex = numChars - 1;
string literalValue = format.Substring(startIndex);
if (foundEscapedDelimiter)
literalValue = ReplaceEscapes(literalValue);
return new Fragment(FragmentType.Literal, literalValue);
}
/// <summary>
/// Replaces escaped brackets, turning '{{' and '}}' into '{' and '}', respectively.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
static string ReplaceEscapes(string value)
{
return value.Replace("{{", "{").Replace("}}", "}");
}
private enum FragmentType
{
Literal,
FormatItem
}
private class Fragment
{
public Fragment(FragmentType type, string value)
{
Type = type;
Value = value;
}
public FragmentType Type
{
get;
private set;
}
/// <summary>
/// The literal value, or the name of the fragment, depending on fragment type.
/// </summary>
public string Value
{
get;
private set;
}
}
}
here is a simple method for any object:
using System.Text.RegularExpressions;
using System.ComponentModel;
public static string StringWithFormat(string format, object args)
{
Regex r = new Regex(#"\{([A-Za-z0-9_]+)\}");
MatchCollection m = r.Matches(format);
var properties = TypeDescriptor.GetProperties(args);
foreach (Match item in m)
{
try
{
string propertyName = item.Groups[1].Value;
format = format.Replace(item.Value, properties[propertyName].GetValue(args).ToString());
}
catch
{
throw new FormatException("The format string is not valid");
}
}
return format;
}
And here how to use it:
DateTime date = DateTime.Now;
string dateString = StringWithFormat("{Month}/{Day}/{Year}", date);
output : 2/27/2012
I implemented this is a simple class that duplicates the functionality of String.Format (except for when using classes). You can either use a dictionary or a type to define fields.
https://github.com/SergueiFedorov/NamedFormatString
C# 6.0 is adding this functionality right into the language spec, so NamedFormatString is for backwards compatibility.
I solved this in a slightly different way to the existing solutions.
It does the core of the named item replacement (not the reflection bit that some have done). It is extremely fast and simple...
This is my solution:
/// <summary>
/// Formats a string with named format items given a template dictionary of the items values to use.
/// </summary>
public class StringTemplateFormatter
{
private readonly IFormatProvider _formatProvider;
/// <summary>
/// Constructs the formatter with the specified <see cref="IFormatProvider"/>.
/// This is defaulted to <see cref="CultureInfo.CurrentCulture">CultureInfo.CurrentCulture</see> if none is provided.
/// </summary>
/// <param name="formatProvider"></param>
public StringTemplateFormatter(IFormatProvider formatProvider = null)
{
_formatProvider = formatProvider ?? CultureInfo.CurrentCulture;
}
/// <summary>
/// Formats a string with named format items given a template dictionary of the items values to use.
/// </summary>
/// <param name="text">The text template</param>
/// <param name="templateValues">The named values to use as replacements in the formatted string.</param>
/// <returns>The resultant text string with the template values replaced.</returns>
public string FormatTemplate(string text, Dictionary<string, object> templateValues)
{
var formattableString = text;
var values = new List<object>();
foreach (KeyValuePair<string, object> value in templateValues)
{
var index = values.Count;
formattableString = ReplaceFormattableItem(formattableString, value.Key, index);
values.Add(value.Value);
}
return String.Format(_formatProvider, formattableString, values.ToArray());
}
/// <summary>
/// Convert named string template item to numbered string template item that can be accepted by <see cref="string.Format(string,object[])">String.Format</see>
/// </summary>
/// <param name="formattableString">The string containing the named format item</param>
/// <param name="itemName">The name of the format item</param>
/// <param name="index">The index to use for the item value</param>
/// <returns>The formattable string with the named item substituted with the numbered format item.</returns>
private static string ReplaceFormattableItem(string formattableString, string itemName, int index)
{
return formattableString
.Replace("{" + itemName + "}", "{" + index + "}")
.Replace("{" + itemName + ",", "{" + index + ",")
.Replace("{" + itemName + ":", "{" + index + ":");
}
}
It is used in the following way:
[Test]
public void FormatTemplate_GivenANamedGuid_FormattedWithB_ShouldFormatCorrectly()
{
// Arrange
var template = "My guid {MyGuid:B} is awesome!";
var templateValues = new Dictionary<string, object> { { "MyGuid", new Guid("{A4D2A7F1-421C-4A1D-9CB2-9C2E70B05E19}") } };
var sut = new StringTemplateFormatter();
// Act
var result = sut.FormatTemplate(template, templateValues);
//Assert
Assert.That(result, Is.EqualTo("My guid {a4d2a7f1-421c-4a1d-9cb2-9c2e70b05e19} is awesome!"));
}
Hope someone finds this useful!
Even though the accepted answer gives some good examples, the .Inject as well as some of the Haack examples do not handle escaping. Many also rely heavily on Regex (slower), or DataBinder.Eval which is not available on .NET Core, and in some other environments.
With that in mind, I've written a simple state machine based parser that streams through characters, writing to a StringBuilder output, character by character. It is implemented as String extension method(s) and can take both a Dictionary<string, object> or object with parameters as input (using reflection).
It handles unlimited levels of {{{escaping}}} and throws FormatException when input contains unbalanced braces and/or other errors.
public static class StringExtension {
/// <summary>
/// Extension method that replaces keys in a string with the values of matching object properties.
/// </summary>
/// <param name="formatString">The format string, containing keys like {foo} and {foo:SomeFormat}.</param>
/// <param name="injectionObject">The object whose properties should be injected in the string</param>
/// <returns>A version of the formatString string with keys replaced by (formatted) key values.</returns>
public static string FormatWith(this string formatString, object injectionObject) {
return formatString.FormatWith(GetPropertiesDictionary(injectionObject));
}
/// <summary>
/// Extension method that replaces keys in a string with the values of matching dictionary entries.
/// </summary>
/// <param name="formatString">The format string, containing keys like {foo} and {foo:SomeFormat}.</param>
/// <param name="dictionary">An <see cref="IDictionary"/> with keys and values to inject into the string</param>
/// <returns>A version of the formatString string with dictionary keys replaced by (formatted) key values.</returns>
public static string FormatWith(this string formatString, IDictionary<string, object> dictionary) {
char openBraceChar = '{';
char closeBraceChar = '}';
return FormatWith(formatString, dictionary, openBraceChar, closeBraceChar);
}
/// <summary>
/// Extension method that replaces keys in a string with the values of matching dictionary entries.
/// </summary>
/// <param name="formatString">The format string, containing keys like {foo} and {foo:SomeFormat}.</param>
/// <param name="dictionary">An <see cref="IDictionary"/> with keys and values to inject into the string</param>
/// <returns>A version of the formatString string with dictionary keys replaced by (formatted) key values.</returns>
public static string FormatWith(this string formatString, IDictionary<string, object> dictionary, char openBraceChar, char closeBraceChar) {
string result = formatString;
if (dictionary == null || formatString == null)
return result;
// start the state machine!
// ballpark output string as two times the length of the input string for performance (avoids reallocating the buffer as often).
StringBuilder outputString = new StringBuilder(formatString.Length * 2);
StringBuilder currentKey = new StringBuilder();
bool insideBraces = false;
int index = 0;
while (index < formatString.Length) {
if (!insideBraces) {
// currently not inside a pair of braces in the format string
if (formatString[index] == openBraceChar) {
// check if the brace is escaped
if (index < formatString.Length - 1 && formatString[index + 1] == openBraceChar) {
// add a brace to the output string
outputString.Append(openBraceChar);
// skip over braces
index += 2;
continue;
}
else {
// not an escaped brace, set state to inside brace
insideBraces = true;
index++;
continue;
}
}
else if (formatString[index] == closeBraceChar) {
// handle case where closing brace is encountered outside braces
if (index < formatString.Length - 1 && formatString[index + 1] == closeBraceChar) {
// this is an escaped closing brace, this is okay
// add a closing brace to the output string
outputString.Append(closeBraceChar);
// skip over braces
index += 2;
continue;
}
else {
// this is an unescaped closing brace outside of braces.
// throw a format exception
throw new FormatException($"Unmatched closing brace at position {index}");
}
}
else {
// the character has no special meaning, add it to the output string
outputString.Append(formatString[index]);
// move onto next character
index++;
continue;
}
}
else {
// currently inside a pair of braces in the format string
// found an opening brace
if (formatString[index] == openBraceChar) {
// check if the brace is escaped
if (index < formatString.Length - 1 && formatString[index + 1] == openBraceChar) {
// there are escaped braces within the key
// this is illegal, throw a format exception
throw new FormatException($"Illegal escaped opening braces within a parameter - index: {index}");
}
else {
// not an escaped brace, we have an unexpected opening brace within a pair of braces
throw new FormatException($"Unexpected opening brace inside a parameter - index: {index}");
}
}
else if (formatString[index] == closeBraceChar) {
// handle case where closing brace is encountered inside braces
// don't attempt to check for escaped braces here - always assume the first brace closes the braces
// since we cannot have escaped braces within parameters.
// set the state to be outside of any braces
insideBraces = false;
// jump over brace
index++;
// at this stage, a key is stored in current key that represents the text between the two braces
// do a lookup on this key
string key = currentKey.ToString();
// clear the stringbuilder for the key
currentKey.Clear();
object outObject;
if (!dictionary.TryGetValue(key, out outObject)) {
// the key was not found as a possible replacement, throw exception
throw new FormatException($"The parameter \"{key}\" was not present in the lookup dictionary");
}
// we now have the replacement value, add the value to the output string
outputString.Append(outObject);
// jump to next state
continue;
} // if }
else {
// character has no special meaning, add it to the current key
currentKey.Append(formatString[index]);
// move onto next character
index++;
continue;
} // else
} // if inside brace
} // while
// after the loop, if all braces were balanced, we should be outside all braces
// if we're not, the input string was misformatted.
if (insideBraces) {
throw new FormatException("The format string ended before the parameter was closed.");
}
return outputString.ToString();
}
/// <summary>
/// Creates a Dictionary from an objects properties, with the Key being the property's
/// name and the Value being the properties value (of type object)
/// </summary>
/// <param name="properties">An object who's properties will be used</param>
/// <returns>A <see cref="Dictionary"/> of property values </returns>
private static Dictionary<string, object> GetPropertiesDictionary(object properties) {
Dictionary<string, object> values = null;
if (properties != null) {
values = new Dictionary<string, object>();
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(properties);
foreach (PropertyDescriptor prop in props) {
values.Add(prop.Name, prop.GetValue(properties));
}
}
return values;
}
}
Ultimately, all the logic boils down into 10 main states - For when the state machine is outside a bracket and likewise inside a bracket, the next character is either an open brace, an escaped open brace, a closed brace, an escaped closed brace, or an ordinary character. Each of these conditions is handled individually as the loop progresses, adding characters to either an output StringBuffer or a key StringBuffer. When a parameter is closed, the value of the key StringBuffer is used to look up the parameter's value in the dictionary, which then gets pushed into the output StringBuffer. At the end, the value of the output StringBuffer is returned.
string language = "Python";
int numquotes = 2;
string output = language + " has "+ numquotes + " language types.";
Edit:
What I should have said was, "No, I don't believe what you want to do is supported by C#. This is as close as you are going to get."

Categories

Resources