Windows Powershell - удалить строку за номером строки - PullRequest
0 голосов
/ 07 января 2019

У меня большой CSV-файл (1,6 ГБ). как я могу удалить конкретную строку, например линия 1005?

1 Ответ

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

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

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

Get-Content file.txt | 
  Where-Object ReadCount -ne 1005 |
    Set-Content -Encoding Utf8 new-file.txt

Get-Content добавляет (несколько непонятно названное) свойство .ReadCount к каждой строке, которую оно выводит, которая содержит номер строки на основе 1.

  • Обратите внимание, что кодировка символов входного файла не сохраняется Get-Content, поэтому вы должны явно контролировать Set-Content 'выходную кодировку, как показано выше, используя UTF-8 в качестве примера.

  • Не считывая весь файл в память целиком, вы должны вывести в новый файл, хотя бы временно; Вы можете заменить исходный файл временным файлом вывода на
    Move-Item -Force new-file.txt file.txt


A более быстрая, но интенсивная память альтернатива , основанная на прямом использовании .NET Framework, которая также позволяет обновлять файл на месте:

$file = 'file.txt'
$lines = [IO.File]::ReadAllLines("$PWD/$file")
Set-Content -Encoding UTF8 $file -Value $lines[0..1003 + 1005..($lines.Count-1)]
  • Обратите внимание на необходимость использования "$PWD/$file", т. Е. Для явного добавления предварительного пути к текущему каталогу к относительному пути, хранящемуся в $file, поскольку представление платформы .NET о том, чем является текущий каталог, отличается от представления PowerShell.

    • Хотя $lines = Get-Content $file будет функционально эквивалентно $lines = [IO.File]::ReadAllLines("$PWD/$file"), он будет работать заметно хуже.
  • 0..1003 создает массив индексов от 0 до 1003; + объединяет этот массив с индексами 1005 через оставшуюся часть входного массива; обратите внимание, что индексы массива основаны на 0, а номера строк - 1.

  • Также обратите внимание, как результирующий массив передается в Set-Content в качестве прямого аргумента через -Value, что быстрее, чем передача его по конвейеру (... | Set-Content ...), где элемент будет выполнена обработка по элементам.


Наконец, удобный для памяти метод, который быстрее, чем метод на основе конвейера :

$file = 'file.txt'
$outFile = [IO.File]::CreateText("$PWD/new-file.txt")
$lineNo = 0
try {
  foreach ($line in [IO.File]::ReadLines("$PWD/$file")) {
    if (++$lineNo -eq 1005) { continue }
    $outFile.WriteLine($line)
  }
} finally {
  $outFile.Dispose()
}

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

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