13 Stimmen

Beste Methode zum Parsen einer Kette von E-Mail-Adressen

Ich arbeite also mit einigen E-Mail-Kopfdaten, und für die Felder to:, from:, cc: und bcc: können die E-Mail-Adressen auf verschiedene Weise ausgedrückt werden:

First Last <name@domain.com>
Last, First <name@domain.com>
name@domain.com

Und diese Varianten können in ein und derselben Nachricht in beliebiger Reihenfolge in einer durch Komma getrennten Zeichenfolge erscheinen:

First, Last <name@domain.com>, name@domain.com, First Last <name@domain.com>

Ich habe versucht, mit einem Weg zu kommen, um diese Zeichenfolge in separate Vorname, Nachname, E-Mail für jede Person zu analysieren (den Namen weglassen, wenn nur eine E-Mail-Adresse angegeben wird).

Kann mir jemand sagen, wie ich das am besten anstellen soll?

Ich habe versucht, die Kommas zu trennen, was auch funktioniert, außer im zweiten Beispiel, wo der Nachname an erster Stelle steht. Ich nehme an, dass diese Methode funktionieren könnte, wenn ich nach der Aufteilung jedes Element untersuche und prüfe, ob es ein '@' oder '<'/'>' enthält; wenn dies nicht der Fall ist, könnte angenommen werden, dass das nächste Element der Vorname ist. Ist dies ein guter Weg, dies zu tun? Habe ich ein anderes Format übersehen, in dem die Adresse vorliegen könnte?


UPDATE: Vielleicht sollte ich ein wenig zu klären, im Grunde alles, was ich suchen zu tun ist, brechen Sie die Zeichenfolge, die die mehrere Adressen in einzelne Zeichenfolgen, die die Adresse in welchem Format es gesendet wurde. Ich habe meine eigenen Methoden für die Validierung und Extraktion der Informationen aus einer Adresse, es war nur schwierig für mich, herauszufinden, der beste Weg, um jede Adresse zu trennen.

Hier ist die Lösung, die ich mir für dieses Ziel ausgedacht habe:

String str = "Last, First <name@domain.com>, name@domain.com, First Last <name@domain.com>, \"First Last\" <name@domain.com>";

List<string> addresses = new List<string>();
int atIdx = 0;
int commaIdx = 0;
int lastComma = 0;
for (int c = 0; c < str.Length; c++)
{
    if (str[c] == '@')
        atIdx = c;

    if (str[c] == ',')
        commaIdx = c;

    if (commaIdx > atIdx && atIdx > 0)
    {
        string temp = str.Substring(lastComma, commaIdx - lastComma);
        addresses.Add(temp);
        lastComma = commaIdx;
        atIdx = commaIdx;
    }

    if (c == str.Length -1)
    {
        string temp = str.Substring(lastComma, str.Legth - lastComma);
        addresses.Add(temp);
    }
}

if (commaIdx < 2)
{
    // if we get here we can assume either there was no comma, or there was only one comma as part of the last, first combo
    addresses.Add(str);
}

Der obige Code generiert die einzelnen Adressen, die ich im weiteren Verlauf verarbeiten kann.

7voto

user7984361 Punkte 61

Es gibt interne System.Net.Mail.MailAddressParser Klasse, die die Methode ParseMultipleAddresses die genau das tut, was Sie wollen. Sie können darauf direkt über reflection oder durch den Aufruf von MailMessage.To.Add Methode, die eine Zeichenkette der E-Mail-Liste akzeptiert.

private static IEnumerable<MailAddress> ParseAddress(string addresses)
{
    var mailAddressParserClass = Type.GetType("System.Net.Mail.MailAddressParser");
    var parseMultipleAddressesMethod = mailAddressParserClass.GetMethod("ParseMultipleAddresses", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
    return (IList<MailAddress>)parseMultipleAddressesMethod.Invoke(null, new object[0]);
}

    private static IEnumerable<MailAddress> ParseAddress(string addresses)
    {
        MailMessage message = new MailMessage();
        message.To.Add(addresses);
        return new List<MailAddress>(message.To); //new List, because we don't want to hold reference on Disposable object
    }

4voto

Michael L Perry Punkte 7177

Auf die Gefahr hin, zwei Probleme zu verursachen, könnten Sie einen regulären Ausdruck erstellen, der auf jedes Ihrer E-Mail-Formate passt. Verwenden Sie "|", um die Formate innerhalb dieser einen Regex zu trennen. Dann können Sie ihn über Ihre Eingabezeichenfolge laufen lassen und alle Übereinstimmungen herausziehen.

public class Address
{
    private string _first;
    private string _last;
    private string _name;
    private string _domain;

    public Address(string first, string last, string name, string domain)
    {
        _first = first;
        _last = last;
        _name = name;
        _domain = domain;
    }

    public string First
    {
        get { return _first; }
    }

    public string Last
    {
        get { return _last; }
    }

    public string Name
    {
        get { return _name; }
    }

    public string Domain
    {
        get { return _domain; }
    }
}

[TestFixture]
public class RegexEmailTest
{
    [Test]
    public void TestThreeEmailAddresses()
    {
        Regex emailAddress = new Regex(
            @"((?<last>\w*), (?<first>\w*) <(?<name>\w*)@(?<domain>\w*\.\w*)>)|" +
            @"((?<first>\w*) (?<last>\w*) <(?<name>\w*)@(?<domain>\w*\.\w*)>)|" +
            @"((?<name>\w*)@(?<domain>\w*\.\w*))");
        string input = "First, Last <name@domain.com>, name@domain.com, First Last <name@domain.com>";

        MatchCollection matches = emailAddress.Matches(input);
        List<Address> addresses =
            (from Match match in matches
             select new Address(
                 match.Groups["first"].Value,
                 match.Groups["last"].Value,
                 match.Groups["name"].Value,
                 match.Groups["domain"].Value)).ToList();
        Assert.AreEqual(3, addresses.Count);

        Assert.AreEqual("Last", addresses[0].First);
        Assert.AreEqual("First", addresses[0].Last);
        Assert.AreEqual("name", addresses[0].Name);
        Assert.AreEqual("domain.com", addresses[0].Domain);

        Assert.AreEqual("", addresses[1].First);
        Assert.AreEqual("", addresses[1].Last);
        Assert.AreEqual("name", addresses[1].Name);
        Assert.AreEqual("domain.com", addresses[1].Domain);

        Assert.AreEqual("First", addresses[2].First);
        Assert.AreEqual("Last", addresses[2].Last);
        Assert.AreEqual("name", addresses[2].Name);
        Assert.AreEqual("domain.com", addresses[2].Domain);
    }
}

Dieser Ansatz hat mehrere Nachteile. Einer ist, dass die Zeichenfolge nicht validiert wird. Wenn Sie Zeichen in der Zeichenfolge haben, die nicht in eines der von Ihnen gewählten Formate passen, werden diese Zeichen einfach ignoriert. Ein weiterer Nachteil ist, dass die akzeptierten Formate alle an einer Stelle aufgeführt sind. Sie können keine neuen Formate hinzufügen, ohne die monolithische Regex zu ändern.

4voto

Phil Haselden Punkte 2666

Ihr 2. E-Mail-Beispiel ist keine gültige Adresse, da es ein Komma enthält, das sich nicht innerhalb einer Anführungszeichenfolge befindet. Um gültig zu sein, sollte sie wie folgt lauten: "Last, First"<name@domain.com> .

Was das Parsing betrifft, so könnten Sie, wenn Sie etwas ganz Strenges wollen, Folgendes verwenden System.Net.Mail.MailAddressCollection .

Wenn Sie nur Ihre Eingabe in einzelne E-Mail-Strings aufteilen möchten, sollte der folgende Code funktionieren. Er ist nicht sehr streng, behandelt aber Kommas in Anführungszeichen und löst eine Ausnahme aus, wenn die Eingabe ein nicht geschlossenes Anführungszeichen enthält.

public List<string> SplitAddresses(string addresses)
{
    var result = new List<string>();

    var startIndex = 0;
    var currentIndex = 0;
    var inQuotedString = false;

    while (currentIndex < addresses.Length)
    {
        if (addresses[currentIndex] == QUOTE)
        {
            inQuotedString = !inQuotedString;
        }
        // Split if a comma is found, unless inside a quoted string
        else if (addresses[currentIndex] == COMMA && !inQuotedString)
        {
            var address = GetAndCleanSubstring(addresses, startIndex, currentIndex);
            if (address.Length > 0)
            {
                result.Add(address);
            }
            startIndex = currentIndex + 1;
        }
        currentIndex++;
    }

    if (currentIndex > startIndex)
    {
        var address = GetAndCleanSubstring(addresses, startIndex, currentIndex);
        if (address.Length > 0)
        {
            result.Add(address);
        }
    }

    if (inQuotedString)
        throw new FormatException("Unclosed quote in email addresses");

    return result;
}

private string GetAndCleanSubstring(string addresses, int startIndex, int currentIndex)
{
    var address = addresses.Substring(startIndex, currentIndex - startIndex);
    address = address.Trim();
    return address;
}

4voto

Sarel Botha Punkte 12053

Es gibt keine einfache Lösung für dieses Problem. Ich würde empfehlen, einen kleinen Zustandsautomaten zu erstellen, der Zeichen für Zeichen liest und die Arbeit auf diese Weise erledigt. Wie Sie sagten, wird die Aufteilung durch Komma nicht immer funktionieren.

Mit einem Zustandsautomaten können Sie alle Möglichkeiten abdecken. Ich bin sicher, es gibt noch viele andere, die Sie noch nicht gesehen haben. Zum Beispiel: "Erster Letzter"

Schauen Sie sich den RFC dazu an, um alle Möglichkeiten zu entdecken. Tut mir leid, ich kenne die Zahl nicht. Wahrscheinlich gibt es mehrere, da sich diese Art von Dingen weiterentwickelt.

2voto

Scott Dorman Punkte 41206

Hierfür gibt es keine allgemeine, einfache Lösung. Der gewünschte RFC lautet RFC2822 in dem alle möglichen Konfigurationen einer E-Mail-Adresse beschrieben werden. Das Beste, was Sie bekommen werden, ist richtig ist es, einen zustandsbasierten Tokenizer zu implementieren, der den im RFC festgelegten Regeln folgt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X