2015-09-01 2 views
1

В настоящее время у меня есть следующая строка кода.Альтернатива Get-Content

(Get-Content 'file.txt') | 
    ForEach-Object {$_ -replace '"', ''} | 
    Set-Content 'file.txt' 

Это работало при тестировании, но теперь я пытаюсь использовать его на файлы реальных данных (13 ГБ) и этот процесс с помощью Get-Content вызывает Powershell потреблять большое количество оперативной памяти и в конечном итоге все доступная оперативная память на машине.

Есть ли лучший способ добиться того же результата без того же объема накладных расходов?

Кажется, я делаю противоположность лучшей практике, но не уверен, что еще будет чище/меньше оперативной памяти, чем выше.

+0

Название не сделало бы очевидным, если вы уже не знали способ решения этой проблемы, но это дублировать. http://stackoverflow.com/questions/4192072/how-to-process-a-file-in-powershell-line-by-line-as-a-stream – EBGreen

+1

Возможный дубликат [Как обработать файл в строке PowerShell -by-line как поток] (http://stackoverflow.com/questions/4192072/how-to-process-a-file-in-powershell-line-by-line-as-a-stream) – EBGreen

ответ

4

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

$file = New-Object System.IO.StreamReader -Arg "c:\test\file.txt" 
$outstream = [System.IO.StreamWriter] "c:\test\out.txt" 

while ($line = $file.ReadLine()) { 
    $s = $line -replace '"', '' 
    $outstream.WriteLine($s) 
} 
$file.close() 
$outstream.close() 
+0

Есть ли особая причина, по которой вы выбрали разные методы для создания читателя и писателя? –

+0

Использование метода Stream Reader, похоже, делает трюк. Очень быстро, и использование памяти не превышало 1% один раз. – ricky89

+0

Я попытался проверить это на 1GB файл журнала .etx, и по какой-то причине он оставил около 3 МБ в файл. – mjolinor

1

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

Get-content 'file.txt' -ReadCount 5000 | 
foreach-object {$_ -replace '"', '' | 
add-content 'newfile.txt' } 
+0

Hi mjolinor , Спасибо за предложение. Я попробовал установить 5000 для начала и несколько раз, когда набор Readcount был установлен ниже, но powershell разбился каждый раз. Get-content не подходит для ситуации. – ricky89

+0

Я использовал это много раз, чтобы читать большие файлы с хорошими результатами. Определите «сбой». – mjolinor

+0

Использование памяти продолжало расти (хотя и медленнее, чем я изначально имел), и в конечном итоге powershell стал не реагирующим и закрытым. – ricky89

5

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

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

Get-Content 'C:\path\to\file.txt' | 
    ForEach-Object {$_ -replace '"', ''} | 
    Set-Content 'C:\path\to\temp.txt' 

Remove-Item 'C:\path\to\file.txt' 
Rename-Item 'C:\path\to\temp.txt' 'file.txt' 

Выполнение этого позволяет избежать исчерпания памяти, которое вы наблюдали. Обработку можно ускорить, увеличив количество отсчетов, как предлагалось @mjolinor (сократить время выполнения до 40% в моих тестах).

Для еще более эффективного использования производительности своего подхода с StreamReader и StreamWriter, что @campbell.rw предложили:

$reader = New-Object IO.StreamReader 'C:\path\to\file.txt' 
$writer = New-Object IO.StreamWriter 'C:\path\to\temp.txt' 

while ($reader.Peek() -ge 0) { 
    $line = $reader.ReadLine().Replace('"', '') 
    $writer.WriteLine($line) 
} 

$reader.Close(); $reader.Dispose() 
$writer.Close(); $writer.Dispose() 

Remove-Item 'C:\path\to\file.txt' 
Rename-Item 'C:\path\to\temp.txt' 'file.txt' 
+1

Метод .peek() для проверки того, не имеет ли у вас в EOF проблемы с завершением цикла, если он попадает в нулевую строку. – mjolinor

+0

Это правильный ответ, так как принятый не учитывает пустые строки. – kuujinbo

Смежные вопросы