Создайте небольшой файл (.txt или .TMP) из огромного файла .TMP

Ошибка "System.OutOfMemoryException" при создании небольшого файла из большого файла.

Обычно я использую приведенную ниже команду PowerShell для создания небольшой версии огромного файла,

Get-Content input_file_name.Tmp -TotalCount 100 | Out-File -Encoding Default "output_file_name_100.Tmp"

Однако это вызывает ошибку System.OutOfMemoryException. Какие-нибудь советы по этому поводу?

Примечание: раньше это работало для файлов большего размера. Думаю, проблема не в размере файла.

1 ответ

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

Get-Content при использовании в конвейере считывает строки из файла по одной за раз.

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

Есть только три сценария, гдеGet-Contentсчитывает весь файл в память:

  • Если вы захватите Get-Contentвывод в переменной ($content = Get-Content ...), и в этом случае переменная получает массив, состоящий из всех строк.

  • Если вы заключите Get-Content вызывать (...), $(...), или @(...), который также возвращает массив всех строк.

  • Если вы используете -Raw переключатель, который делает Get-Contentвернуть одну многострочную строку.


С помощью -TotalCount 100 (или -First 100) не меняет этого фундаментального поведения: после 100 строки были прочитаны, Get-Content прекращает чтение и закрывает файл.

Поэтому код в вашем вопросе не объясняет ваш симптом - у вас не должно быть нехватки памяти - по крайней мере, не потому, что входной файл большой; если это все еще происходит, возможно, вы видите ошибку.

Если у вас есть воспроизводимый случай, я рекомендую вам сообщить об ошибке на форуме Windows PowerShell UserVoice или, если вы можете (также) воспроизвести ошибку в PowerShell [Core] v6+, в репозитории PowerShell Core GitHub.


А пока вы можете рассмотреть возможность использования.NET напрямую, что также обычнобыстрее, чем использование командлетов PowerShell:

[Linq.Enumerable]::Take([IO.File]::ReadLines("$PWD/input_file_name.Tmp"), 100) |
  Out-File -Encoding Default output_file_name_100.Tmp

Примечание:
• Использование"$PWD/"как часть пути к входному файлу, поскольку рабочий каталог.NET обычно отличается от каталога PowerShell.
• В литералах типа PowerShell ([...]), System.часть полного имени типа может быть опущена; таким образом[Linq.Enumerable] относится к System.Linq.Enumerable, а также [IO.File] к System.IO.File

Другие вопросы по тегам