Разделить строку в PowerShell по шаблону - PullRequest
0 голосов
/ 23 октября 2018

У меня довольно длинная строка в PowerShell, которую нужно разделить.Каждый раздел начинается с даты в формате mm/dd/yyyy hh:mm:ss AM.По сути, я пытаюсь получить самое последнее сообщение в строке.Мне не нужно сохранять часть даты / времени, поскольку она уже есть в другом месте.

Вот как выглядит строка:

10/20/2018 1:22:33 AM
Some message the first one in the string

It can be several lines long
With multiple line breaks

But this is still the first message in the string

10/21/2018 4:55:11 PM
This would be second message
Same type of stuff

But its a different message

Я знаю, как разбить строку наконкретные символы, но я не знаю, как на шаблоне, как дата / время.

Ответы [ 4 ]

0 голосов
/ 23 октября 2018

При условии, что секции [datetime] идут по возрастанию,
должно быть достаточно разделить их на RegEx и получить последний

((Get-Content .\test.txt -Raw) -split "\d+/\d+/\d{4} \d+:\d+:\d+ [AP]M`r?`n")[-1]

Вывод на основе строки образца, сохраненной в файле test.txt

This would be second message
Same type of stuff

But its a different message
0 голосов
/ 23 октября 2018

Примечание:

  • В приведенном ниже решении предполагается, что сечение не обязательно в хронологическом порядке , поэтому необходимо проверить все метки времени для определения самой последней.
  • Если, напротив, вы можете предположить, что сообщение last является самым последним, используйте намного более простой ответ LotPings .

Если вы заранее не знаете, какой раздел имеет самую последнюю временную отметку, лучше всего подходит построчный подход:

$dtMostRecent = [datetime] 0
# Split the long input string ($longString) into lines and iterate over them.
# If input comes from a file, replace 
#   $longString -split '\r?\n'
# with
#   Get-Content file.txt
# If the file is large, replace the whole command with
#   Get-Content file.txt | ForEach-Object { ... } 
# and replace $line with $_ in the script block (loop body).
foreach ($line in $longString -split '\r?\n') {
  # See if the line at hand contains (only) a date.     
  if ($dt = try { [datetime] $line } catch {}) {
    # See if the date at hand is the most recent so far.
    $isMostRecent = $dt -ge $dtMostRecent
    if ($isMostRecent) {
      # Save this time stamp as the most recent one and initialize the
      # array to collect the following lines in (the message).
      $dtMostRecent = $dt 
      $msgMostRecentLines = @()
    }
  } elseif ($isMostRecent) {
    # Collect the lines of the message associated with the most recent date.
    $msgMostRecentLines += $line
  }
}

# Convert the message lines back into a single, multi-line string.
# $msgMostRecent now contains the multi-line message associated with
# the most recent time stamp.
$msgMostRecent = $msgMostRecentLines -join "`n"

Обратите внимание, как используется try { [datetime] $line } catch {}попытаться преобразовать строку в [datetime] экземпляр и завершиться с ошибкой, если это невозможно, в этом случае $dt назначается $null, что в логическом контексте интерпретируется как $False.

Этот метод работает независимо от используемой в настоящее время культуры, потому что при преобразовании PowerShell всегда используется инвариантная культура при преобразовании из строк, а даты во входных данных находятся в одном из форматов, которые понимает инвариантная культура.

Напротив, оператор -as, использование которого было бы здесь более удобным - $dt =$line -as [datetime] - неожиданно является чувствительным к культуре, как указывает Esperento57 .
Это удивительное поведение дискаиспользуется в этом выпуске GitHub .

0 голосов
/ 23 октября 2018

Насколько мне известно, вы не можете использовать для этого статические методы String, такие как Split ().Я пытался найти регулярное выражение, которое бы обрабатывало все это, но не смогло найти ничего, что могло бы его правильно разбить.

Итак, вам нужно идти строка за строкой,тестирование, чтобы увидеть, является ли эта строка датой, затем объединить строки между ними, как показано ниже:

$fileContent = Get-Content "inputFile.txt"
$messages = @()
$currentMessage = [string]::Empty
foreach($line in $fileContent)
{
    if ([Regex]::IsMatch($line, "\d{1,2}/\d{1,2}/\d{4} \d{1,2}:\d{2}:\d{2} (A|P)M"))
    {
        # The current line is a date, the current message is complete
        # Add the current message to the output, and clear out the old message 
        # from your temporary storage variable $currentMessage
        if (-not [string]::IsNullOrEmpty($currentMessage))
        {
            $messages += $currentMessage
            $currentMessage = [string]::Empty
        }
    }
    else
    {
        # Add this line to the message you're building.
        # Include a new line character, as it was stripped out with Get-Content
        $currentMessage += "$line`n"
    }
}

# Add the last message to the output
$messages += $currentMessage

# Do something with the message
Write-Output $messages

Поскольку ключом ко всему этому является признание того, что данная строка является датой и, следовательно, началомсообщения, давайте посмотрим немного подробнее на регулярное выражение.«\ d» будет соответствовать любому десятичному символу 0–9, а непосредственно следующие фигурные скобки указывают количество десятичных символов, которые должны соответствовать.Таким образом, \ d {1,2} означает «искать один или два десятичных знака» или, в данном случае, месяц года.Затем мы ищем «/», еще 1 или 2 десятичных знака - «\ d {1,2}», еще один «/», а затем ровно 4 десятичных знака - «\ d {4}».Время больше того же самого, с ":" между десятичными знаками вместо "/".В конце будет либо «AM», либо «PM», поэтому мы ищем либо «A», либо «P», за которым следует «M», что в качестве регулярного выражения равно «(A | P) M».

Объедините все это, и вы получите "\ d {1,2} / \ d {1,2} / \ d {4} \ d {1,2}: \ d {2}: \ d {2} (A | P) M ", чтобы определить, есть ли у вас дата в этой строке.Я полагаю, что было бы также возможно использовать [DateTime] :: Parse (), чтобы определить, является ли строка датой, но тогда вы не получите удовольствия от Regex и нуждаетесь в try-catch.Для получения дополнительной информации о Regex в Powershell (которые являются просто .NET Regex) см. .NET Regex Quick Reference

0 голосов
/ 23 октября 2018

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

$arr =  $str -split  "[0-9]{1,2}/[0-9]{1,2}/[0-9]{1,4} [0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2} [AaPp]M\n"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...