Как скопировать определенный текст из тела письма? - PullRequest
0 голосов
/ 14 января 2019
Option Explicit

Sub GetFromInbox()

Dim olApp As Outlook.Application
Dim olNs As Outlook.Namespace
Dim olFldr As Outlook.MAPIFolder
Dim olItms As Outlook.Items
Dim olMail As Variant
Dim i As Long

Set olApp = New Outlook.Application
Set olNs = olApp.GetNamespace("MAPI")
Set olFldr = olNs.GetDefaultFolder(olFolderInbox).Folders("impMail")
Set olItms = olFldr.Items

olItms.Sort "Subject"

For Each olMail In olItms
    If InStr(olMail.Subject, "SubjectoftheEmail") > 0 Then
        ThisWorkbook.Sheets("Fixings").Cells(2, 2).Value = olMail.Body

    End If
Next olMail

Set olFldr = Nothing
Set olNs = Nothing
Set olApp = Nothing
End Sub

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

Этот E-mail предназначен только для внутреннего использования

Привет

@ ABC4: пожалуйста, добавьте в систему следующую информацию (для 12-янв-2019 ):

12345_ABC_MakOpt --- 264532154.78
12345_ABC_GAPFee --- 145626547.80

спасибо

´ ---------------------------------------------- ------- 'получить настройки «------------------------------------------------- ----

    Dim wb As Workbook
    Dim rngEmailSubject As Range
    Dim rngInstrumentName As Range
    Dim rngDate As Range
    Dim rngAmount As Range
    Dim arrFixing() As typFixing
    Dim rngValue As Range

    Dim rowIdx As Integer
    Dim ix As Integer
    Dim fixingDate As Date

    With wb.Sheets("FixingFromEmail")

        Set rngInstrumentName = .Range("instrument.name")
        Set rngDate = .Range("Date")
        Set rngAmount = .Range("Amount")

        rowIdx = rngInstrumentName.Row
        ix = 0

        Do While True

            rowIdx = rowIdx + 1
             If Not IsEmpty(.Cells(rowIdx, rngInstrumentName.Column).Value) _
        Then

                ix = ix + 1

                ReDim Preserve arrFixing(1 To ix)
                arrFixing(ix).InstrumentName = .Cells(rowIdx, rngInstrumentName.Column).Value
                arrFixing(ix).Date = .Cells(rowIdx, rngDate.Column).Value
                arrFixing(ix).Amount = .Cells(rowIdx, rngAmount.Column).Value


            Else
                Exit Do
            End If

        Loop

    End With´

Ответы [ 2 ]

0 голосов
/ 14 января 2019

Ваш вопрос слишком расплывчат для конкретного ответа. Все, что я могу предложить, это руководство по первым этапам.

Вам необходимо решить, что является фиксированным, а что переменным.

Исправлено ли «@ ABC4»? «@ ABC4: пожалуйста, добавьте в систему следующую информацию (для»)?

Всегда ли есть две строки данных? Есть ли несколько строк данных, примерами которых они являются? Является ли формат этих строк:

Xxxxxxx space hyphen hyphen hyphen space amount 

Я бы начал с разделения текста на строки. Почти наверняка строки нарушены переводом каретки-возврата. Для проверки:

Dim Count As Long

For Each olMail In olItms

  Debug.Print Replace(Replace(Mid$(olMailBody, 1, 200), vbCr, "{c}"), vbLf, "{l}" & vbLf)
  Count = Count + 1
  If Count >= 10 Then
    Exit For
  End If

Next olMail

Вывод будет выглядеть примерно как десять (максимум) копий:

@ABC4: please add the following detail in system (for 12-Jan-2019):{c}{l}
{c}{l}
12345_ABC_MakOpt --- 264532154.78{c}{l}
12345_ABC_GAPFee --- 145626547.80{c}{l}
Are the characters between lines “{c}{l}” or “{l}” or something else?

В приведенном ниже коде замените vbCR & vbLf, если необходимо, затем запустите его:

Dim Count As Long
Dim InxL As Long
Dim Lines() As String

For Each olMail In olItms

  Lines = Split(olMail.Body, vbCR & vbLf)
  For InxL = 0 to UBound(Lines)
    Debug.Print InxL + 1 & "  " & Lines(InxL)
  Next
  Count = Count + 1
  If Count >= 10 Then
    Exit For
  End If

Next

Вывод будет выглядеть примерно как десять (максимум) копий:

0  
1  @ABC4: please add the following detail in system (for 12-Jan-2019):
2  
3  12345_ABC_MakOpt --- 264532154.78
4  12345_ABC_GAPFee --- 145626547.80
5 

Теперь вы можете видеть текстовое тело в виде линий. Примечание: первая строка - номер 0. Вверху никогда не бывает пустой строки? Вверху всегда есть пустая строка? Это меняется? Я собираюсь предположить, что всегда есть пустая строка сверху. Следующий код нуждается в модификации, если это предположение неверно.

Если в строке 1 указано «xxxxxxxxxx date):», вы можете извлечь дату так:

Dim DateCrnt As Date
Dim Pos As Long

DateCrnt = CDate(Left$(Right$(Lines(1), 13), 11))

или

Pos = InStr(1, Lines(1), "(for ")
DateCrnt = CDate(Mid$(Lines(1), Pos + 5, 11))

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

Теперь вы можете разбить строки данных с помощью кода, подобного следующему:

Dim NameCrnt As String
Dim AmtCrnt As Double

For InxL = 3 To UBound(Lines)
  If Lines(InxL) <> "" Then
    Pos = InStr(1, Lines(InxL), " --- ")
    If Pos = 0 Then
      Debug.Assert False   ' Line not formatted as expected
    Else
      NameCrnt = Mid$(Lines(InxL), 1, Pos - 1)
      AmtCrnt = Mid$(Lines(InxL), Pos + 5)
    End If
    Debug.Print "Date="& DateCrnt & "    " & "Name=" & NameCrnt & "   " & "Amount=" & AmtCrnt
  End If
Next

Вывод:

Date=12/01/2019    Name=12345_ABC_MakOpt   Amount=264532154.78
Date=12/01/2019    Name=12345_ABC_GAPFee   Amount=145626547.8

Новый раздел, показывающий, как добавить данные из электронной почты на лист

Это вторая версия этого раздела, потому что ОП передумал насчет необходимого формата.

Код ниже был протестирован, но с поддельными электронными письмами, которые я создал, чтобы быть похожим на тот, что в вашем вопросе. Так что, возможно, потребуется некоторая отладка.

Я создал новую рабочую книгу и новую рабочую таблицу под названием «Исправления» со следующими заголовками:

Empty worksheet before macro run

После обработки моих поддельных электронных писем лист выглядел следующим образом:

Worksheet after runs to add data from three daily emails

Последовательность строк зависит от последовательности, в которой были найдены электронные письма. Вы, вероятно, хотите сначала новые. Сортировка листа выходит за рамки этого ответа. Примечание: именно заголовки столбцов сообщают макросу, какие значения должны быть записаны. Если в электронное письмо была добавлена ​​новая строка, добавьте новый заголовок столбца, и значение будет сохранено без изменения макроса.

За одним исключением, я не буду объяснять операторы VBA, которые я использовал, потому что легко найти в Интернете «VBA xxxxx» и найти спецификацию для оператора xxxxx. Исключением является использование двух коллекций для хранения ожидающих данных. Остальные объяснения описывают причины моего подхода.

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

Const ColFixDate As Long = 1
Const ColFixDataFirst As Long = 2
Const RowFixHead As Long = 1
Const RowFixDataFirst As Long = 2

Я мог бы написать Cells(Row, 1).Value = Date. Это имеет два недостатка: (1) если столбец даты когда-либо перемещается, вы должны искать в коде операторы, которые обращаются к нему, и (2) вы должны помнить, что находится в столбце 1, 2 или 3, делая ваш код труднее читать. Я избегаю использования литералов для номеров строк или столбцов. Дополнительные усилия по вводу ColFixDataFirst вместо 2 быстро окупаются.

Я заметил, что в коде, добавленном к вашему вопросу, вы используете именованные диапазоны для достижения того же эффекта. Проблема с VBA состоит в том, что часто есть несколько способов достижения того же самого эффекта. Я предпочитаю постоянные, но каждый из нас должен выбирать свои собственные фавориты.

ПоработавВ отделе, который обрабатывал много электронных писем и рабочих книг, полученных от посторонних, которые содержали полезные данные, я могу вам сказать, что их форматы постоянно меняются. Там будет лишняя пустая строка или будет удалена существующая. Там будут дополнительные данные или существующие данные будут в другой последовательности. Авторы вносят изменения, которые, по их мнению, будут полезны, но редко делают что-то полезное, например, спрашивают, хотят ли получатели изменения или даже предупреждают их об этом. Худшее, что я когда-либо видел, было, когда два числовых столбца были перепутаны, и это не было замечено в течение многих месяцев К счастью, я не был вовлечен, потому что это был страшный сон, когда мы возвращали ошибочные данные из нашей системы и затем импортировали правильные данные. Я проверяю все, что могу придумать, и отказываюсь обрабатывать электронные письма, которые не соответствуют моим ожиданиям. Все сообщения об ошибках записываются в непосредственное окно, которое удобно во время разработки. Вы можете использовать MsgBox или записать их в файл. Если письмо успешно обработано, оно не удаляется; он перемещается в подпапку, поэтому его можно извлечь, если он когда-либо понадобится снова.

olMail является константой Outlook. Не используйте olMail или любое другое зарезервированное слово в качестве имени переменной.

Я использовал Session вместо NameSpace. Предполагается, что они эквивалентны, но однажды у меня была проблема с пространством имен, которую я не смог диагностировать, поэтому я больше не использую их.

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

Я обрабатываю электронные письма в обратном порядке, потому что они доступны по позиции. Например, если электронная почта 5 перемещена в другую папку, предыдущая электронная почта 6 теперь является электронной почтой 5, и цикл For пропускает ее. Если электронные письма обрабатываются в обратном порядке, вы не возражаете, что электронная почта 6 теперь является электронной почтой 5, потому что вы уже обработали эту электронную почту.

Если вы не установите NumberFormat ячеек, содержащих даты или суммы, они будут отображаться в соответствии с настройками Microsoft по умолчанию для вашей страны. Я использовал мои любимые форматы отображения. Поменяй на любимую.

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

Вернись с вопросами о чем-то еще, что ты не понимаешь.

Option Explicit
Sub GetFromInbox()

  Const ColFixDate As Long = 1
  Const ColFixName As Long = 2
  Const ColFixAmt As Long = 3
  Const RowFixDataFirst As Long = 2

  Dim AmtCrnt As Double
  Dim ColFixCrnt As Long
  Dim DateCrnt As Date
  Dim ErrorOnEmail As Boolean
  Dim Found As Boolean
  Dim InxItem As Long
  Dim InxLine As Long
  Dim InxPend As Long
  Dim Lines() As String
  Dim NameCrnt As String
  Dim olApp As New Outlook.Application
  Dim olFldrIn As Outlook.Folder
  Dim olFldrOut As Outlook.Folder
  Dim olMailCrnt As Outlook.MailItem
  Dim PendingAmts As Collection
  Dim PendingNames As Collection
  Dim Pos As Long
  Dim RowFixCrnt As Long
  Dim StateEmail As Long
  Dim TempStg As String
  Dim WshtFix As Worksheet

  Set WshtFix = ThisWorkbook.Worksheets("Fixings")
  With WshtFix
    RowFixCrnt = .Cells(Rows.Count, ColFixDate).End(xlUp).Row + 1
  End With

  Set olApp = New Outlook.Application
  Set olFldrIn = olApp.Session.GetDefaultFolder(olFolderInbox).Folders("impMail")
  Set olFldrOut = olFldrIn.Folders("Processed")

  For InxItem = olFldrIn.Items.Count To 1 Step -1

    If olFldrIn.Items(InxItem).Class = Outlook.olMail Then

      Set olMailCrnt = olFldrIn.Items(InxItem)

      If InStr(olMailCrnt.Subject, "SubjectoftheEmail") > 0 Then
        Lines = Split(olMailCrnt.Body, vbCr & vbLf)

        'For InxLine = 0 To UBound(Lines)
        '  Debug.Print InxLine + 1 & "  " & Lines(InxLine)
        'Next

        StateEmail = 0    ' Before "please add ..." line
        ErrorOnEmail = False
        Set PendingAmts = Nothing
        Set PendingNames = Nothing
        Set PendingAmts = New Collection
        Set PendingNames = New Collection

        For InxLine = 0 To UBound(Lines)
          NameCrnt = ""     ' Line is not a data line
          Lines(InxLine) = Trim(Lines(InxLine))  ' Remove any leading or trailing spaces

          ' Extract data from line
          If Lines(InxLine) <> "" Then
            If StateEmail = 0 Then
              If InStr(1, Lines(InxLine), "please add the ") = 0 Then
                Debug.Print "Error with email received " & olMailCrnt.ReceivedTime & vbLf & _
                            "  The first non-blank line is" & vbLf & _
                            "    " & Lines(InxLine) & vbLf & _
                            "  but I was expecting something like:" & vbLf & _
                            "    @ABC4: please add the following detail in system (for 13-Jan-2019):"
                ErrorOnEmail = True
                Exit For
              End If
              TempStg = Left$(Right$(Lines(InxLine), 13), 11)
              If Not IsDate(TempStg) Then
                Debug.Print "Error with email received " & olMailCrnt.ReceivedTime & vbLf & _
                            "  The value I extracted from the ""please add the ...""" & _
                            " line is """ & vbLf & "  " & TempStg & _
                            """ which I do not recognise as a date"
                ErrorOnEmail = True
                Exit For
              End If
              DateCrnt = CDate(TempStg)
              StateEmail = 1    ' After "please add ..." line
            ElseIf StateEmail = 1 Then
              If Lines(InxLine) = "" Then
                ' Ignore blank line
              ElseIf Lines(InxLine) = "thanks" Then
                ' No more data lines
                Exit For
              Else
                Pos = InStr(1, Lines(InxLine), " --- ")
                If Pos = 0 Then
                  Debug.Print "Error with email received " & olMailCrnt.ReceivedTime & vbLf & _
                              "  Data line: " & Lines(InxLine) & vbLf & _
                              "    does not contain ""---"" as required"
                  ErrorOnEmail = True
                  'Debug.Assert False
                  Exit For
                End If
                NameCrnt = Mid$(Lines(InxLine), 1, Pos - 1)
                TempStg = Mid$(Lines(InxLine), Pos + 5)
                If Not IsNumeric(TempStg) Then
                  Debug.Print "Error with email received " & olMailCrnt.ReceivedTime & vbLf & _
                              "  Data line:" & Lines(InxLine) & vbLf & _
                              "    value after ""---"" is not an amount"
                  ErrorOnEmail = True
                  'Debug.Assert False
                  Exit For
                End If
                AmtCrnt = CDbl(TempStg)
              End If
            End If  ' StateEmail
          End If ' Lines(InxLine) <> ""

          If ErrorOnEmail Then
            ' Ignore any remaining lines
            Exit For
          End If

          If NameCrnt <> "" Then
            ' Line was a data line without errors. Save until know entire email is error free
            PendingNames.Add NameCrnt
            PendingAmts.Add AmtCrnt
          End If

        Next InxLine

        If Not ErrorOnEmail Then
          ' Output pending rows now know entire email is error-free
          With WshtFix
            For InxPend = 1 To PendingNames.Count
              With .Cells(RowFixCrnt, ColFixDate)
                .Value = DateCrnt
                .NumberFormat = "d mmm yy"
              End With
              .Cells(RowFixCrnt, ColFixName).Value = PendingNames(InxPend)
              With .Cells(RowFixCrnt, ColFixAmt)
                .Value = PendingAmts(InxPend)
                .NumberFormat = "#,##0.00"
              End With
              RowFixCrnt = RowFixCrnt + 1
            Next
          End With
          ' Move fully processed email to folder Processed
          olMailCrnt.Move olFldrOut
        End If

      End If  ' InStr(olMailCrnt.Subject, "SubjectoftheEmail") > 0
    End If  ' olFldrIn.Items(InxItem).Class = Outlook.olMail

  Next InxItem

  Set olFldrIn = Nothing
  Set olFldrOut = Nothing
  olApp.Quit
  Set olApp = Nothing

End Sub
0 голосов
/ 14 января 2019

Если у вас всегда есть дата в первой строке, вы можете получить это с помощью чего-то простого: [0-9] {2} - [A-Za-Z] {3} - [0-9] {4} * * +1001

Попробуйте это на regex101, чтобы увидеть, что делают отдельные части регулярного выражения

Что касается другой части, я думаю, что самый простой способ - прочитать всю строку

...