Après avoir demandé une related question, je pris conscience d'une meilleure méthode:
/// <summary>
/// Extracts email addresses in the following formats:
/// "Tom W. Smith" <[email protected]>
/// "Smith, Tom" <[email protected]>
/// Tom W. Smith <[email protected]>
/// [email protected]
/// Multiple emails can be separated by a comma or semicolon.
/// Watch out for <see cref="FormatException"/>s when enumerating.
/// </summary>
/// <param name="value">Collection of emails in the accepted formats.</param>
/// <returns>
/// A collection of <see cref="System.Net.Mail.MailAddress"/>es.
/// </returns>
/// <exception cref="ArgumentException">Thrown if the value is null, empty, or just whitespace.</exception>
public static IEnumerable<MailAddress> ExtractEmailAddresses(this string value)
{
if (string.IsNullOrWhiteSpace(value)) throw new ArgumentException("The arg cannot be null, empty, or just whitespace.", "value");
// Remove commas inside of quotes
value = value.Replace(';', ',');
var emails = value.SplitWhilePreservingQuotedValues(',');
var mailAddresses = emails.Select(email => new MailAddress(email));
return mailAddresses;
}
/// <summary>
/// Splits the string while preserving quoted values (i.e. instances of the delimiter character inside of quotes will not be split apart).
/// Trims leading and trailing whitespace from the individual string values.
/// Does not include empty values.
/// </summary>
/// <param name="value">The string to be split.</param>
/// <param name="delimiter">The delimiter to use to split the string, e.g. ',' for CSV.</param>
/// <returns>A collection of individual strings parsed from the original value.</returns>
public static IEnumerable<string> SplitWhilePreservingQuotedValues(this string value, char delimiter)
{
Regex csvPreservingQuotedStrings = new Regex(string.Format("(\"[^\"]*\"|[^{0}])+", delimiter));
var values =
csvPreservingQuotedStrings.Matches(value)
.Cast<Match>()
.Select(m => m.Value.Trim())
.Where(v => !string.IsNullOrWhiteSpace(v));
return values;
}
Cette méthode passe les tests suivants:
[TestMethod]
public void ExtractEmails_SingleEmail_Matches()
{
string value = "[email protected]";
var expected = new List<MailAddress>
{
new MailAddress("[email protected]"),
};
var actual = value.ExtractEmailAddresses();
CollectionAssert.AreEqual(expected, actual.ToList());
}
[TestMethod()]
public void ExtractEmails_JustEmailCSV_Matches()
{
string value = "[email protected]; [email protected]";
var expected = new List<MailAddress>
{
new MailAddress("[email protected]"),
new MailAddress("[email protected]"),
};
var actual = value.ExtractEmailAddresses();
CollectionAssert.AreEqual(expected, actual.ToList());
}
[TestMethod]
public void ExtractEmails_MultipleWordNameThenEmailSemicolonSV_Matches()
{
string value = "a a a <[email protected]>; a a a <[email protected]>";
var expected = new List<MailAddress>
{
new MailAddress("a a a <[email protected]>"),
new MailAddress("a a a <[email protected]>"),
};
var actual = value.ExtractEmailAddresses();
CollectionAssert.AreEqual(expected, actual.ToList());
}
[TestMethod]
public void ExtractEmails_JustEmailsSemicolonSV_Matches()
{
string value = "[email protected]; [email protected]";
var expected = new List<MailAddress>
{
new MailAddress("[email protected]"),
new MailAddress("[email protected]"),
};
var actual = value.ExtractEmailAddresses();
CollectionAssert.AreEqual(expected, actual.ToList());
}
[TestMethod]
public void ExtractEmails_NameInQuotesWithCommaThenEmailsCSV_Matches()
{
string value = "\"a, a\" <[email protected]>; \"a, a\" <[email protected]>";
var expected = new List<MailAddress>
{
new MailAddress("\"a, a\" <[email protected]>"),
new MailAddress("\"a, a\" <[email protected]>"),
};
var actual = value.ExtractEmailAddresses();
CollectionAssert.AreEqual(expected, actual.ToList());
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ExtractEmails_EmptyString_Throws()
{
string value = string.Empty;
var actual = value.ExtractEmailAddresses();
}
[TestMethod]
[ExpectedException(typeof(FormatException))]
public void ExtractEmails_NonEmailValue_ThrowsOnEnumeration()
{
string value = "a";
var actual = value.ExtractEmailAddresses();
actual.ToList();
}
Même si, comme naasking dit, le MailAddressCollection ne supporte effectivement des virgules dans les chaînes entre guillemets, si elle renvoie une exception au format ne permet pas d'identifier exactement où l'erreur dans la chaîne est. Donc, cette réponse est bonne pour cette raison aussi: en séparant d'abord les emails, vous arrivez à identifier où le problème est susceptible de se produire, car il s'agit d'exceptions levées sur des constructeurs MailAddress individuels. – Gavin