как отсортировать текстовый файл в определенном порядке в Powershell - PullRequest
0 голосов
/ 15 сентября 2018

У меня есть этот первый текст, например

today is sunny in the LA 
and the temperature is 21C

today is cloudy in the NY 
and the temperature is 18C

today is sunny in the DC 
and the temperature is 25C

и это порядок, который я хочу:

18C 
25C
21C

Я хочу изменить первый файл в том же порядке, что и второй, но ничего не удаляя:

today is cloudy in the NY
and the temperature is 18C

today is sunny in the DC 
and the temperature is 25C

today is sunny in the LA
and the temperature is 21C

Ответы [ 3 ]

0 голосов
/ 15 сентября 2018

Примечание : Приведенное ниже решение PSv3 + отвечает на другой вопрос: он сортирует абзацы численно по содержащимся в них значениям температуры, а не в внешне прописанный заказ.

  • Как таковой, он все еще может представлять интерес, учитывая общее название вопроса.
  • Ответ на вопрос в ответ на вопрос см. в моем другом сообщении .

Вот краткое решение, но обратите внимание, что оно требует считывания входного файла в память в целом (в любом случае, Sort-Object собирает все свои входные объекты также в памяти, так как он не использует временные файлы для ослабления потенциала давление памяти):

((Get-Content -Raw file.txt) -split '\r?\n\r?\n' -replace '\r?\n$' |
  Sort-Object { [int] ($_ -replace '(?s).+ (\d+)C$', '$1') }) -join 
    [Environment]::NewLine * 2
  • (Get-Content -Raw file.txt) считывает входной файл в память целиком как одну многострочную строку.

  • -split '\r?\n\r?\n' разбивает многострочную строку на массив абзацев (блоки строк, разделенных пустой строкой), а -replace '\r?\n$' удаляет завершающий символ новой строки, если таковой имеется, из абзаца в самом конце файла.

    • Regex \r?\n соответствует как CRLF в стиле Windows, так и в новых строках только для LF в стиле Unix.
  • Sort-Object { [int] ($_ -replace '(?s).+ (\d+)C$', '$1') }) численно сортирует абзацы по номеру температуры в конце каждого абзаца (например, 18).

    • $_ представляет входной абзац под рукой.
    • -replace '...', '...' выполняет замену строки на основе регулярного выражения, которое в этом случае извлекает строку с номером температуры из конца абзаца.
    • Cast [int] преобразует числовую строку в целое число для правильной числовой сортировки.
  • -join [Environment]::NewLine * 2 собирает отсортированные абзацы в одну многострочную строку с абзацами, разделенными пустой строкой.

    • [Environment]::NewLine - соответствующая платформе последовательность новой строки; альтернативно вы можете жестко закодировать новые строки как "`r`n" (CRLF) или "`n" (LF).

Вы можете отправить вывод в новый файл, добавив что-то вроде
... | Set-Content sortedFile.txt (что делает файл ANSI-кодированным в Windows PowerShell и UTF-8-кодированным в PowerShell Core по умолчанию; при необходимости используйте -Encoding).

Поскольку весь входной файл считывается в память заранее, позволяет записывать результаты непосредственно обратно во входной файл (... | Set-Content file.txt), но при этом существует небольшой риск потери данных. а именно, если запись прервана до завершения.

0 голосов
/ 16 сентября 2018

Полезный ответ Nas работает, но это операция O (m * n); то есть при наличии m абзацев для вывода в заданном порядке и n входных абзацев требуется выполнить m * n операций; если все входные абзацы должны быть выведены (в установленном порядке), т. е. если m равно n, усилие равно квадратичному .

Следующее решение PSv4 + будет масштабироваться лучше, поскольку для него требуется только линейное , а не квадратичное усилие:

# The tokens prescribing the sort order, which may come from 
# another file read with Get-Content, for instance.
$tokensToSortBy = '18C', '25C', '21C'

# Create a hashtable that indexes the input file's paragraphs by the sort
# token embedded in each.
((Get-Content -Raw file.txt) -split '\r?\n\r?\n' -replace '\r?\n$').ForEach({
  $htParagraphsBySortToken[$_ -replace '(?s).* (\d+C)$(?:\r?\n)?', '$1'] = $_
})

# Loop over the tokens prescribing the sort order, and retrieve the
# corresponding paragraph, then reassemble the paragraphs into a single,
# multi-line string with -join
$tokensToSortBy.ForEach({ $htParagraphsBySortToken[$_] }) -join [Environment]::NewLine * 2
  • (Get-Content -Raw file.txt) считывает входной файл в память целиком как одну многострочную строку.

  • -split '\r?\n\r?\n' разбивает многострочную строку на массив абзацев (блоки строк, разделенных пустой строкой), а -replace '\r?\n$' удаляет завершающий символ новой строки, если таковой имеется, из абзаца в самом конце файла.

    • Regex \r?\n совпадает как с CRLF в стиле Windows, так и с новыми строками LF только для Unix.
  • $_ -replace '(?s).* (\d+C)$(?:\r?\n)?', '$1' извлекает маркер сортировки (например, 25C) из каждого абзаца, который становится ключом хеш-таблицы.

    • $_ представляет входной абзац под рукой.
    • -replace '...', '...' выполняет замену строки на основе регулярного выражения.
  • -join [Environment]::NewLine * 2 собирает отсортированные абзацы в одну многострочную строку с абзацами, разделенными пустой строкой.

    • [Environment]::NewLine - соответствующая платформе последовательность новой строки; альтернативно вы можете жестко закодировать новые строки как "`r`n" (CRLF) или "`n" (LF).

Вы можете отправить вывод в новый файл, добавив что-то вроде
... | Set-Content sortedFile.txt до последнего оператора (что делает файл «ANSI» кодированным в Windows PowerShell и UTF-8 в PowerShell Core по умолчанию; при необходимости используйте -Encoding.

0 голосов
/ 15 сентября 2018
$text = Get-Content -path C:\text.txt
$order = '18C','25C','21C'

foreach ($item in $order)
{
    $text | ForEach-Object {
        if ($_ -match "$item`$") { # `$ to match string at the end of the line
            Write-Output $text[($_.ReadCount-2)..($_.ReadCount)] # output lines before and after match
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...