Разбор многострочного письма в var - PullRequest
1 голос
/ 01 апреля 2019

Я пытаюсь разобрать многострочную электронную почту, чтобы я мог получить данные, которые находятся на отдельной новой строке под заголовком в теле письма.Это выглядит так:

EMAIL STARTING IN APRIL

Marketing ID                                     Local Number
-------------------                              ----------------------
GR332230                                         0000232323

Dispatch Code                                    Logic code
-----------------                                -------------------
GX3472                                           1

Destination ID                                   Destination details
-----------------                                -------------------
3411144

Кажется, я получаю все в каждом окне сообщений, когда использую readline для чтения строк, хотя все, что мне нужно, это данные под каждым ------, как показано

Это мой код:

foreach (MailItem mail in publicFolder.Items)
{
    if (mail != null)                  
    {

        if (mail is MailItem)
        {

            MessageBox.Show(mail.Body, "MailItem body");
            // Creates new StringReader instance from System.IO
            using (StringReader reader = new StringReader(mail.Body))
            {
                string line;
                while ((line = reader.ReadLine()) !=null) 
                //Loop over the lines in the string.
                if (mail.Body.Contains("Marketing ID"))
                {
                    // var localno = mail.Body.Substring(247,15);//not correct approach

                    // MessageBox.Show(localrefno);
                    //MessageBox.Show("found");
                    //var conexid = mail.Body.Replace(Environment.NewLine);

                    var regex = new Regex("<br/>", RegexOptions.Singleline);


                    MessageBox.Show(line.ToString());
                }
            }


            //var stringBuilder = new StringBuilder();

            //foreach (var s in mail.Body.Split(' '))
            //{
            //    stringBuilder.Append(s).AppendLine();
            //}
            //MessageBox.Show(stringBuilder.ToString());



        }
        else
        {
            MessageBox.Show("Nothing found for MailItem");
        }
    }
}    

Вы можете видеть, что у меня было много попыток, даже с использованием позиции подстроки и использованием регулярных выражений.Пожалуйста, помогите мне получить данные из каждой строки под ---.

Ответы [ 4 ]

1 голос
/ 01 апреля 2019
  var dict = new Dictionary<string, string>();
            try
            {
                var lines = email.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                int starts = 0, end = 0, length = 0;
                while (!lines[starts + 1].StartsWith("-")) starts++;
                for (int i = starts + 1; i < lines.Length; i += 3)
                {
                    var mc = Regex.Matches(lines[i], @"(?:^| )-");
                    foreach (Match m in mc)
                    {
                        int start = m.Value.StartsWith(" ") ? m.Index + 1 : m.Index;
                        end = start;
                        while (lines[i][end++] == '-' && end < lines[i].Length - 1) ;
                        length = Math.Min(end - start, lines[i - 1].Length - start);
                        string key = length > 0 ? lines[i - 1].Substring(start, length).Trim() : "";
                        end = start;
                        while (lines[i][end++] == '-' && end < lines[i].Length) ;
                        length = Math.Min(end - start, lines[i + 1].Length - start);
                        string value = length > 0 ? lines[i + 1].Substring(start, length).Trim() : "";
                        dict.Add(key, value);
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception("Email is not in correct format");
            }

Демонстрация в реальном времени

Использование регулярных выражений:

     var dict = new Dictionary<string, string>();
        try
        {
            var lines = email.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            int starts = 0;
            while (!lines[starts + 1].StartsWith("-")) starts++;
            for (int i = starts + 1; i < lines.Length; i += 3)
            {
                var keys = Regex.Matches(lines[i - 1], @"(?:^| )(\w+\s?)+");
                var values = Regex.Matches(lines[i + 1], @"(?:^| )(\w+\s?)+");
                if (keys.Count == values.Count)
                    for (int j = 0; j < keys.Count; j++)

                        dict.Add(keys[j].Value.Trim(), values[j].Value.Trim());
                else // remove bug if value of first key in a line has no value
                {
                    if (lines[i + 1].StartsWith(" "))
                    {
                        dict.Add(keys[0].Value.Trim(), "");
                        dict.Add(keys[1].Value.Trim(), values[0].Value.Trim());
                    }
                    else
                    {
                        dict.Add(keys[0].Value, values[0].Value.Trim());
                        dict.Add(keys[1].Value.Trim(), "");
                    }
                }

            }
        }
        catch (Exception ex)
        {
            throw new Exception("Email is not in correct format");
        }

Демонстрация в реальном времени

0 голосов
/ 01 апреля 2019

Это не очень хорошая идея делать с Regex, потому что довольно легко забыть крайние случаи, не легко понять и не легко отладить. Это довольно легко попасть в ситуацию, когда Regex зависает ваш процессор и время ожидания. (Я пока не могу комментировать другие ответы. Поэтому, пожалуйста, проверьте, по крайней мере, мои два других случая, прежде чем выбрать окончательное решение.)

В ваших случаях для вашего примера работает следующее решение Regex. Однако существуют некоторые дополнительные ограничения: Вам необходимо убедиться, что в столбце без начального или бесконечного столбца нет пустых значений. Или, скажем, если есть более двух столбцов, и любой из них в середине пуст, из-за этого имена и значения этой строки не будут совпадать.

К сожалению, я не могу дать вам решение не-Regex, потому что я не знаю спецификацию, например: будут ли пустые места? Будут ли там вкладки? У каждого поля есть фиксированное количество символов, или они будут гибкими? Если он гибкий и может иметь пустые значения, какие правила обнаруживать, какие столбцы пусты? Я предполагаю, что вполне возможно, что они определяются длиной имени столбца и будут иметь только пробел в качестве разделителя. Если это так, то есть два способа решить эту проблему: два прохода Regex или написать свой собственный парсер. Если бы все поля имели фиксированную длину, это было бы еще проще: просто используя подстроку, чтобы обрезать линии, а затем обрезать их.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

public class Program
{
    public class Record{
        public string Name {get;set;}
        public string Value {get;set;}
    }

    public static void Main()
    {
        var regex = new Regex(@"(?<name>((?!-)[\w]+[ ]?)*)(?>(?>[ \t]+)?(?<name>((?!-)[\w]+[ ]?)+)?)+(?:\r\n|\r|\n)(?>(?<splitters>(-+))(?>[ \t]+)?)+(?:\r\n|\r|\n)(?<value>((?!-)[\w]+[ ]?)*)(?>(?>[ \t]+)?(?<value>((?!-)[\w]+[ ]?)+)?)+", RegexOptions.Compiled);
        var testingValue =
@"EMAIL STARTING IN APRIL

Marketing ID                                     Local Number
-------------------                              ----------------------
GR332230                                         0000232323

Dispatch Code                                    Logic code
-----------------                                -------------------
GX3472                                           1

Destination ID                                   Destination details
-----------------                                -------------------
3411144";
        var matches = regex.Matches(testingValue);

        var rows = (
            from match in matches.OfType<Match>()
            let row = (
                from grp in match.Groups.OfType<Group>()
                select new {grp.Name, Captures = grp.Captures.OfType<Capture>().ToList()}
            ).ToDictionary(item=>item.Name, item=>item.Captures.OfType<Capture>().ToList())
            let names = row.ContainsKey("name")? row["name"] : null
            let splitters = row.ContainsKey("splitters")? row["splitters"] : null
            let values = row.ContainsKey("value")? row["value"] : null
            where names != null && splitters != null &&
                names.Count == splitters.Count &&
                (values==null || values.Count <= splitters.Count)
            select new {Names = names, Values = values}
            );

        var records = new List<Record>();
        foreach(var row in rows)
        {
            for(int i=0; i< row.Names.Count; i++)
            {
                records.Add(new Record{Name=row.Names[i].Value, Value=i < row.Values.Count ? row.Values[i].Value : ""});
            }
        }

        foreach(var record in records)
        {
            Console.WriteLine(record.Name + " = " + record.Value);
        }
    }
}

выход:

Marketing ID  = GR332230 
Local Number = 0000232323
Dispatch Code  = GX3472 
Logic code = 1
Destination ID  = 3411144
Destination details =

Обратите внимание, что это также работает для такого рода сообщений: Электронная почта, начинающаяся в апреле

Marketing ID                                     Local Number
-------------------                              ----------------------
GR332230                                         0000232323

Dispatch Code                                    Logic code
-----------------                                -------------------
GX3472                                           1

Destination ID                                   Destination details
-----------------                                -------------------
                                                 3411144

выход:

Marketing ID  = GR332230 
Local Number = 0000232323
Dispatch Code  = GX3472 
Logic code = 1
Destination ID  = 
Destination details = 3411144

Или это:

EMAIL STARTING IN APRIL

Marketing ID                                     Local Number
-------------------                              ----------------------


Dispatch Code                                    Logic code
-----------------                                -------------------
GX3472                                           1

Destination ID                                   Destination details
-----------------                                -------------------
                                                 3411144               

выход:

Marketing ID  = 
Local Number = 
Dispatch Code  = GX3472 
Logic code = 1
Destination ID  = 
Destination details = 3411144
0 голосов
/ 01 апреля 2019

Вот подход, если вы не нуждаетесь в заголовках, информация поступает в порядке и обязательно. Это не будет работать для данных с пробелами или необязательными полями.

foreach (MailItem mail in publicFolder.Items)
{
  MessageBox.Show(mail.Body, "MailItem body");
  // Split by line, remove dash lines.
  var data = Regex.Split(mail.Body, @"\r?\n|\r")
    .Where(l => !l.StartsWith('-'))
    .ToList();
  // Remove headers
  for(var i = data.Count -2; lines >= 0; i -2)
  {
    data.RemoveAt(i);
  }
  // now data contains only the info you want in the order it was presented.
  // Asuming info doesn't have spaces.
  var result = data.SelectMany(d => d.Split(' '));
  // WARNING: Missing info will not be present.
  // {"GR332230", "0000232323", "GX3472", "1", "3411144"}
}
0 голосов
/ 01 апреля 2019

Вот моя попытка.Я не знаю, может ли формат электронной почты измениться (строки, столбцы и т. Д.).

Я не могу придумать простой способ разделения столбцов, кроме проверки на наличие двойного пробела (мое решение).

class Program
{
    static void Main(string[] args)
    {
        var emailBody = GetEmail();
        using (var reader = new StringReader(emailBody))
        {
            var lines = new List<string>();
            const int startingRow = 2; // Starting line to read from (start at Marketing ID line)
            const int sectionItems = 4; // Header row (ex. Marketing ID & Local Number Line) + Dash Row + Value Row + New Line

            // Add all lines to a list
            string line = "";
            while ((line = reader.ReadLine()) != null)
            {
                lines.Add(line.Trim()); // Add each line to the list and remove any leading or trailing spaces
            }

            for (var i = startingRow; i < lines.Count; i += sectionItems)
            {
                var currentLine = lines[i];
                var indexToBeginSeparatingColumns = currentLine.IndexOf("  "); // The first time we see double spaces, we will use as the column delimiter, not the best solution but should work

                var header1 = currentLine.Substring(0, indexToBeginSeparatingColumns);
                var header2 = currentLine.Substring(indexToBeginSeparatingColumns, currentLine.Length - indexToBeginSeparatingColumns).Trim();

                currentLine = lines[i+2]; //Skip dash line
                indexToBeginSeparatingColumns = currentLine.IndexOf("  ");

                string value1 = "", value2 = "";
                if (indexToBeginSeparatingColumns == -1) // Use case of there being no value in the 2nd column, could be better
                {
                    value1 = currentLine.Trim();
                }
                else
                {
                    value1 = currentLine.Substring(0, indexToBeginSeparatingColumns);
                    value2 = currentLine.Substring(indexToBeginSeparatingColumns, currentLine.Length - indexToBeginSeparatingColumns).Trim();
                }                    

                Console.WriteLine(string.Format("{0},{1},{2},{3}", header1, value1, header2, value2));
            }
        }
    }

    static string GetEmail()
    {
        return @"EMAIL STARTING IN APRIL

                Marketing ID                                     Local Number
                -------------------                              ----------------------
                GR332230                                         0000232323

                Dispatch Code                                    Logic code
                -----------------                                -------------------
                GX3472                                           1

                Destination ID                                   Destination details
                -----------------                                -------------------
                3411144";
    }
}

Вывод выглядит примерно так:

Маркетинговый идентификатор, GR332230, Локальный номер, 0000232323 Код отправки, GX3472, Логический код, 1 идентификатор пункта назначения, 3411144, Подробности пункта назначения,

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...