Разбор текста EML с регулярным выражением
Не могли бы вы помочь мне, пожалуйста, проанализируйте текст EML с регулярным выражением.
Я хочу получить отдельно:
1). текст между Content-Transfer-Encoding: base64 и -=_ альтернативой, если есть строка выше Content-Type: text / html
2). текст между Content-Transfer-Encoding: base64 и -=_related, если есть две строки над строкой Content-Type: image/jpeg
Посмотрите, пожалуйста, на мир кода в powershell:
$text = @"
--=_alternative XXXXXXXXXXXXXX_=
Content-Type: text/html; charset="KOI8-R"
Content-Transfer-Encoding: base64
111111111111111111111111111111111111111111111111111111
--=_alternative XXXXXXXXXXXXXX_=
Content-Type: text/html; charset="KOI8-R"
Content-Transfer-Encoding: base64
222222222222222222222222222222222222222222222222222222
--=_alternative XXXXXXXXXXXXXX_=--
--=_related XXXXXXXXXXXXXX_=--_=
Content-Type: image/jpeg
Content-ID: <_2_XXXXXXXXXXXXXX>
Content-Transfer-Encoding: base64
333333333333333333333333333333333333333333333333333333
--=_related XXXXXXXXXXXXXX_=
Content-Type: image/jpeg
Content-ID: <_2_XXXXXXXXXXXXXX>
Content-Transfer-Encoding: base64
444444444444444444444444444444444444444444444444444444
--=_related XXXXXXXXXXXXXX_=
Content-Type: image/jpeg
Content-ID: <_2_XXXXXXXXXXXXXX>
Content-Transfer-Encoding: base64
555555555555555555555555555555555555555555555555555555
--=_related XXXXXXXXXXXXXX_=--
"@
$regex1 = "(?ms).+?Content-Transfer-Encoding: base64(.+?)--=_alternative"
$text1 = ([regex]::Matches($text,$regex1) | foreach {$_.groups[1].value})
Write-Host "text1 : " -fore red
Write-Host $text1
#I want to get as output elements (of array, maybe, or one after another)
#1). text between Content-Transfer-Encoding: base64 and --=_alternative, if there is above line Content-Type: text/html
#this
#1111111111111111111111111111111111111111111111111111111
#then this
#2222222222222222222222222222222222222222222222222222222
$regex2 = "(?ms).+?Content-Transfer-Encoding: base64(.+?)--=_related"
$text2 = ([regex]::Matches($text,$regex2) | foreach {$_.groups[1].value})
#I want to get as output elements (of array, maybe, or one after another)
#2). text between Content-Transfer-Encoding: base64 and --=_related, if there is two lines above line Content-Type: image/jpeg
#this
#3333333333333333333333333333333333333333333333333333333
#then this
#4444444444444444444444444444444444444444444444444444444
#then this
#5555555555555555555555555555555555555555555555555555555
Write-Host "text2 : " -fore red
Write-Host $text2
Спасибо за вашу помощь. Хорошего дня.
PS На основе кода Джесси Уэстлейк, вот небольшая отредактированная версия RegEx, которая работала для меня:
$files = Get-ChildItem -Path "\\<SERVER_NAME>\mailroot\Drop"
Foreach ($file in $files){
$text = Get-Content $file.FullName
$RegexText = '(?:Content-Type: text/html.+?Content-Transfer-Encoding: base64(.+?)(?:--=_))'
$RegexImage = '(?:Content-Type: image/jpeg.+?Content-Transfer-Encoding: base64(.+?)(?:--=_))'
$TextMatches = [Regex]::Matches($text, $RegexText, [System.Text.RegularExpressions.RegexOptions]::Singleline)
$ImageMatches = [Regex]::Matches($text, $RegexImage, [System.Text.RegularExpressions.RegexOptions]::Singleline)
If ($TextMatches[0].Success)
{
Write-Host "Found $($TextMatches.Count) Text Matches:"
Write-Output $TextMatches.ForEach({$_.Groups[1].Value})
}
If ($ImageMatches[0].Success)
{
Write-Host "Found $($ImageMatches.Count) Image Matches:"
Write-Output $ImageMatches.ForEach({$_.Groups[1].Value})
}
}
1 ответ
TL; DR: просто перейдите к коду внизу...
Код ниже довольно уродлив, так что прости меня.
По сути, я просто создал регулярное выражение, которое начинается с Content-Type: text/html
, Это соответствует чему-либо после этого, лениво, пока это не достигает новой строки \n
, возврат каретки \r
или комбинация один за другим \r\n
,
Вы должны заключить их в скобки, чтобы использовать или |
оператор. Мы не хотим на самом деле захватывать / возвращать какие-либо из этих групп, поэтому мы используем синтаксис группы без захвата: (?:text-to-match)
, Мы используем это в другом месте, как вы можете видеть. Вы также можете размещать группы захвата и захвата друг в друге.
Во всяком случае, продолжая. После сопоставления новой строки, мы хотим увидеть Content-Transfer-Encoding: base64
, Кажется, это требуется в каждом из ваших примеров.
После этого мы хотим идентифицировать следующую новую строку, как и в прошлый раз. За исключением этого времени мы хотим сопоставить 1 или более, используя +
, Причина, по которой мы должны сопоставить более одного, состоит в том, что бывают моменты, когда вашим данным, которые вы хотите сохранить, предшествует дополнительная строка. Но так как иногда ему НЕ предшествует дополнительная строка, нам нужно сделать его "ленивым", поставив после плюса знак вопроса +?
,
После этого наступает момент, когда мы будем собирать ваши фактические данные. Это будет первый раз, когда мы используем фактическую группу захвата, а не группу захвата (то есть без знака вопроса, за которым следует двоеточие).
Мы хотим захватить все, что НЕ является новой строкой, потому что иногда за вашими данными следует новая строка, а иногда нет. Не позволяя себе захватывать какие-либо новые строки, это также заставит нашу предыдущую группу сожрать любые дополнительные новые строки, предшествующие нашим данным. Эта группа захвата ([^(?:\n|\n\r)]+)
То, что мы делали там, заключает в себе скобки в скобках, чтобы их охватить. Мы помещаем выражение в квадратные скобки, потому что хотим создать свой собственный "класс" символов. Любой символ внутри скобок будет тем, что ищет наш код. Разница с нашей, однако, в том, что мы поставили карат ^
в качестве первого символа в скобках. Это означает, что НЕ любой из этих персонажей. Очевидно, что мы хотим сопоставить все до следующей строки, поэтому мы хотим захватить все, что не является новой строкой, один или несколько раз, как можно больше раз.
Затем мы проверяем, что наше регулярное выражение привязано к какому-то конечному тексту, поэтому мы продолжаем пытаться сопоставить. Начиная с другой новой строки, совпадающей хотя бы с одним, но так мало, как требуется, чтобы наш захват был успешным (?:\n|\r|\r\n)+?
,
Наконец, мы привязываемся к тому, что мы точно знаем, где мы можем перестать искать наши важные данные. И это --=_
, Я не был уверен, найдем ли мы "альтернативное" слово или "связанный", поэтому я не зашел так далеко. Теперь это сделано.
КЛЮЧ К ЭТОМУ ВСЕМ
Мы не сможем сопоставить новые строки, если не добавим режим регулярного выражения "SingleLine". Чтобы включить это, мы должны использовать язык.NET для создания наших совпадений. Набираем ускорение от [System.Text.RegularExpressions.RegexOptions]
тип. Возможные варианты: "SingleLine" и "MultiLine".
Я создаю отдельное регулярное выражение для text/html
и image/jpeg
поиск. Мы сохраняем результаты этих совпадений в соответствующие им переменные.
Мы можем проверить успешность совпадений, проиндексировав индекс 0, который будет содержать весь объект совпадения, и получить доступ к его .success
свойство, которое возвращает логическое значение. Количество совпадений доступно с .count
имущество. Чтобы получить доступ к определенным группам и захватам, мы должны поставить точки в них после нахождения соответствующего индекса группы захвата. Поскольку мы используем только одну группу захвата, а остальные не захватывают, у нас будет индекс [0] для всего нашего совпадения текста, а [1] должен содержать совпадение нашей группы захвата. Поскольку это объект, мы должны получить доступ к свойству значения.
Очевидно, что приведенный ниже код потребует вашего $text
переменная для хранения данных для поиска.
$RegexText = '(?:Content-Type: text/html.+?(?:\n|\r|\r\n)Content-Transfer-Encoding: base64(?:\n|\r|\r\n)+?([^(?:\n|\n\r)]+)(?:\n|\r|\r\n)+?(?:\n|\r|\r\n)(?:--=_))'
$RegexImage = '(?:Content-Type: image/jpeg.+?(?:\n|\r|\r\n)Content-Transfer-Encoding: base64(?:\n|\r|\r\n)+?([^(?:\n|\n\r)]+)(?:\n|\r|\r\n)+?(?:\n|\r|\r\n)(?:--=_))'
$TextMatches = [Regex]::Matches($text, $RegexText, [System.Text.RegularExpressions.RegexOptions]::Singleline)
$ImageMatches = [Regex]::Matches($text, $RegexImage, [System.Text.RegularExpressions.RegexOptions]::Singleline)
If ($TextMatches[0].Success)
{
Write-Host "Found $($TextMatches.Count) Text Matches:"
Write-Output $TextMatches.ForEach({$_.Groups[1].Value})
}
If ($ImageMatches[0].Success)
{
Write-Host "Found $($ImageMatches.Count) Image Matches:"
Write-Output $ImageMatches.ForEach({$_.Groups[1].Value})
}
Приведенный выше код приводит к выводу на экран ниже:
Found 2 Text Matches:
111111111111111111111111111111111111111111111111111111
222222222222222222222222222222222222222222222222222222
Found 3 Image Matches:
333333333333333333333333333333333333333333333333333333
444444444444444444444444444444444444444444444444444444
555555555555555555555555555555555555555555555555555555