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.

0voto

richard Punkte 11329

Ich beschloss, bei zwei Einschränkungen einen Schlussstrich zu ziehen:

  1. Die Kopfzeilen To und Cc müssen csv-parsable Strings sein.
  2. Alles, was MailAddress nicht analysieren kann, ist für mich kein Problem.

Ich habe auch beschlossen, dass ich nur an E-Mail-Adressen und nicht an Anzeigenamen interessiert bin, da Anzeigenamen so problematisch und schwer zu definieren sind, während E-Mail-Adressen von mir validiert werden können. Also habe ich MailAddress verwendet, um mein Parsing zu validieren.

Ich habe die Kopfzeilen To und Cc wie eine csv-Zeichenkette behandelt, und auch hier gilt: Alles, was nicht auf diese Weise analysiert werden kann, ist für mich kein Problem.

private string GetProperlyFormattedEmailString(string emailString)
    {
        var emailStringParts = CSVProcessor.GetFieldsFromString(emailString);

        string emailStringProcessed = "";

        foreach (var part in emailStringParts)
        {
            try
            {
                var address = new MailAddress(part);
                emailStringProcessed += address.Address + ",";
            }
            catch (Exception)
            {
                //wasn't an email address
                throw;
            }
        }

        return emailStringProcessed.TrimEnd((','));
    }

EDITAR

Weitere Recherchen haben mir gezeigt, dass meine Annahmen richtig sind. Durchlesen der Spezifikation RFC 2822 zeigt, dass die Felder To, Cc und Bcc csv-parsebare Felder sind. Also ja, es ist schwierig und es gibt eine Menge Probleme, wie bei jedem csv-Parsing, aber wenn Sie einen zuverlässigen Weg haben, um csv-Felder zu parsen (was TextFieldParser im Microsoft.VisualBasic.FileIO-Namensraum ist, und das ist, was ich für diese verwendet), dann sind Sie golden.

Bearbeiten 2

Anscheinend müssen es keine gültigen CSV-Strings sein... die Anführungszeichen bringen alles durcheinander. Ihr CSV-Parser muss also fehlertolerant sein. Ich habe es versuchen, die Zeichenfolge zu parsen, wenn es fehlgeschlagen ist, entfernt er alle Anführungszeichen und versucht es erneut:

public static string[] GetFieldsFromString(string csvString)
    {
        using (var stringAsReader = new StringReader(csvString))
        {
            using (var textFieldParser = new TextFieldParser(stringAsReader))
            {
                SetUpTextFieldParser(textFieldParser, FieldType.Delimited, new[] {","}, false, true);

                try
                {
                    return textFieldParser.ReadFields();
                }
                catch (MalformedLineException ex1)
                {
                    //assume it's not parseable due to double quotes, so we strip them all out and take what we have
                    var sanitizedString = csvString.Replace("\"", "");

                    using (var sanitizedStringAsReader = new StringReader(sanitizedString))
                    {
                        using (var textFieldParser2 = new TextFieldParser(sanitizedStringAsReader))
                        {
                            SetUpTextFieldParser(textFieldParser2, FieldType.Delimited, new[] {","}, false, true);

                            try
                            {
                                return textFieldParser2.ReadFields().Select(part => part.Trim()).ToArray();
                            }
                            catch (MalformedLineException ex2)
                            {
                                return new string[] {csvString};
                            }
                        }
                    }
                }
            }
        }
    }

Das Einzige, was es nicht kann, sind zitierte Konten in einer E-Mail, z. B. "Monkey Header"@stupidemailaddresses.com.

Und hier ist der Test:

[Subject(typeof(CSVProcessor))]
public class when_processing_an_email_recipient_header
{
    static string recipientHeaderToParse1 = @"""Lastname, Firstname"" <firstname_lastname@domain.com>" + "," +
                                           @"<testto@domain.com>, testto1@domain.com, testto2@domain.com" + "," +
                                           @"<testcc@domain.com>, test3@domain.com" + "," +
                                           @"""""Yes, this is valid""""@[emails are hard to parse!]" + "," +
                                           @"First, Last <name@domain.com>, name@domain.com, First Last <name@domain.com>"
                                           ;

    static string[] results1;
    static string[] expectedResults1;

    Establish context = () =>
    {
        expectedResults1 = new string[]
        {
            @"Lastname",
            @"Firstname <firstname_lastname@domain.com>",
            @"<testto@domain.com>",
            @"testto1@domain.com",
            @"testto2@domain.com",
            @"<testcc@domain.com>",
            @"test3@domain.com",
            @"Yes",
            @"this is valid@[emails are hard to parse!]",
            @"First",
            @"Last <name@domain.com>",
            @"name@domain.com",
            @"First Last <name@domain.com>"
        };
    };

    Because of = () =>
    {
        results1 = CSVProcessor.GetFieldsFromString(recipientHeaderToParse1);
    };

    It should_parse_the_email_parts_properly = () => results1.ShouldBeLike(expectedResults1);
}

0voto

Simon Green Punkte 1071

Das habe ich mir ausgedacht. Es geht davon aus, dass eine gültige E-Mail-Adresse nur ein einziges "@"-Zeichen enthalten darf:

    public List<MailAddress> ParseAddresses(string field)
    {
        var tokens = field.Split(',');
        var addresses = new List<string>();

        var tokenBuffer = new List<string>();

        foreach (var token in tokens)
        {
            tokenBuffer.Add(token);

            if (token.IndexOf("@", StringComparison.Ordinal) > -1)
            {
                addresses.Add( string.Join( ",", tokenBuffer));
                tokenBuffer.Clear();
            }
        }

        return addresses.Select(t => new MailAddress(t)).ToList();
    }

-2voto

Ich verwende den folgenden regulären Ausdruck in Java, um die E-Mail-Zeichenfolge von RFC-konformen E-Mail-Adressen zu erhalten:

[A-Za-z0-9]+[A-Za-z0-9._-]+@[A-Za-z0-9]+[A-Za-z0-9._-]+[.][A-Za-z0-9]{2,3}

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