2012-08-23 36 views
14

Một trong những cách để có được không. của các dòng từ một tệp là phương thức này trong powershellpowershell nhận được số dòng lớn (lớn) tập tin

PS C:\Users\Pranav\Desktop\PS_Test_Scripts> $a=Get-Content .\sub.ps1 
PS C:\Users\Pranav\Desktop\PS_Test_Scripts> $a.count 
34 
PS C:\Users\Pranav\Desktop\PS_Test_Scripts> 

Tuy nhiên, khi tôi có tệp văn bản 800 MB lớn, làm cách nào để lấy số dòng từ nó mà không đọc toàn bộ tệp?

Phương pháp trên sẽ tiêu thụ quá nhiều RAM khiến cho tập lệnh bị lỗi hoặc mất quá nhiều thời gian để hoàn thành.

Trả lời

13

Sử dụng Get-Content -Read $nLinesAtTime để đọc phần tập tin của bạn bằng cách phần

$nlines = 0; 
#read file by 1000 lines at a time 
gc $YOURFILE -read 1000 | % { $nlines += $_.Length }; 
[string]::Format("{0} has {1} lines", $YOURFILE, $nlines) 

Và đây là đơn giản nhưng kịch bản chậm để xác nhận làm việc trên tập tin nhỏ

gc $YOURFILE | Measure-Object -Line 
+1

Đó là giá trị chỉ rằng cách tiếp cận thứ hai của bạn chỉ tính các dòng với văn bản. Nếu có dòng trống, chúng không được tính. – Vladislav

8

Điều đầu tiên cần thử là dòng Get-Content và xây dựng dòng đếm một lần, thay vì lưu trữ tất cả các dòng trong một mảng cùng một lúc. Tôi nghĩ rằng điều này sẽ cung cấp hành vi phát trực tuyến thích hợp - tức là toàn bộ tệp sẽ không có trong bộ nhớ cùng một lúc, chỉ dòng hiện tại.

$lines = 0 
Get-Content .\File.txt |%{ $lines++ } 

Và như câu trả lời khác cho thấy, việc thêm -ReadCount có thể tăng tốc độ này.

Nếu điều đó không làm việc cho bạn (nhớ quá chậm hoặc quá nhiều), bạn có thể đi trực tiếp đến một StreamReader:

$count = 0 
$reader = New-Object IO.StreamReader 'c:\logs\MyLog.txt' 
while($reader.ReadLine() -ne $null){ $count++ } 
$reader.Close() # don't forget to do this. Ideally put this in a try/finally block to make sure it happens 
+0

Sử dụng mã IO.StreamReader ở trên đã khắc phục được lỗi bộ nhớ tôi đã nhận được khi sử dụng phương pháp gc bên dưới. Tôi có thể xác nhận rằng nó tiêu thụ ít bộ nhớ hơn nhiều (sử dụng PowerShell 5.0.10514.6) – Fares

1

Đây là điều mà tôi đã viết thư cho cố gắng giảm bớt việc sử dụng bộ nhớ khi phân tích ra khoảng trắng trong tệp txt của tôi. Với điều đó nói rằng, việc sử dụng bộ nhớ vẫn có được loại cao nhưng quá trình mất ít thời gian hơn để chạy. Chỉ để cung cấp cho bạn một số thông tin cơ bản về tệp của tôi, tệp có hơn 2 triệu bản ghi và có không gian màu trắng ở cả hai mặt trước và sau của mỗi dòng. Tôi tin rằng tổng thời gian là 5+ phút Vui lòng cho tôi biết suy nghĩ của bạn nếu có cách cải thiện định dạng. nhờ

$testing = 'C:\Users\something\something\test3.txt' 

$filecleanup = gci $testing 

    foreach ($file in $filecleanup) 
    { $file1 = gc $file -readcount 1000 |foreach{ $_.Trim()} 
    $file1 > $filecleanup} 
9

Dưới đây là một kịch bản Powershell tôi gom góp mà thể hiện một vài phương pháp khác nhau đếm dòng trong một file văn bản, cùng với thời gian và bộ nhớ cần thiết cho từng phương pháp. Kết quả (dưới đây) cho thấy sự khác biệt rõ ràng về thời gian và yêu cầu bộ nhớ. Đối với các thử nghiệm của tôi, có vẻ như điểm nhạy cảm là Get-Content, sử dụng cài đặt ReadCount là 100. Các thử nghiệm khác yêu cầu sử dụng thời gian và/hoặc bộ nhớ đáng kể hơn.

#$testFile = 'C:\test_small.csv' # 245 lines, 150 KB 
#$testFile = 'C:\test_medium.csv' # 95,365 lines, 104 MB 
$testFile = 'C:\test_large.csv' # 285,776 lines, 308 MB 

# Using ArrayList just because they are faster than Powershell arrays, for some operations with large arrays. 
$results = New-Object System.Collections.ArrayList 

function AddResult { 
param([string] $sMethod, [string] $iCount) 
    $result = New-Object -TypeName PSObject -Property @{ 
     "Method" = $sMethod 
     "Count" = $iCount 
     "Elapsed Time" = ((Get-Date) - $dtStart) 
     "Memory Total" = [System.Math]::Round((GetMemoryUsage)/1mb, 1) 
     "Memory Delta" = [System.Math]::Round(((GetMemoryUsage) - $dMemStart)/1mb, 1) 
    } 
    [void]$results.Add($result) 
    Write-Output "$sMethod : $count" 
    [System.GC]::Collect() 
} 

function GetMemoryUsage { 
    # return ((Get-Process -Id $pid).PrivateMemorySize) 
    return ([System.GC]::GetTotalMemory($false)) 
} 

# Get-Content -ReadCount 1 
[System.GC]::Collect() 
$dMemStart = GetMemoryUsage 
$dtStart = Get-Date 
$count = 0 
Get-Content -Path $testFile -ReadCount 1 |% { $count++ } 
AddResult "Get-Content -ReadCount 1" $count 

# Get-Content -ReadCount 10,100,1000,0 
# Note: ReadCount = 1 returns a string. Any other value returns an array of strings. 
# Thus, the Count property only applies when ReadCount is not 1. 
@(10,100,1000,0) |% { 
    $dMemStart = GetMemoryUsage 
    $dtStart = Get-Date 
    $count = 0 
    Get-Content -Path $testFile -ReadCount $_ |% { $count += $_.Count } 
    AddResult "Get-Content -ReadCount $_" $count 
} 

# Get-Content | Measure-Object 
$dMemStart = GetMemoryUsage 
$dtStart = Get-Date 
$count = (Get-Content -Path $testFile -ReadCount 1 | Measure-Object -line).Lines 
AddResult "Get-Content -ReadCount 1 | Measure-Object" $count 

# Get-Content.Count 
$dMemStart = GetMemoryUsage 
$dtStart = Get-Date 
$count = (Get-Content -Path $testFile -ReadCount 1).Count 
AddResult "Get-Content.Count" $count 

# StreamReader.ReadLine 
$dMemStart = GetMemoryUsage 
$dtStart = Get-Date 
$count = 0 
# Use this constructor to avoid file access errors, like Get-Content does. 
$stream = New-Object -TypeName System.IO.FileStream(
    $testFile, 
    [System.IO.FileMode]::Open, 
    [System.IO.FileAccess]::Read, 
    [System.IO.FileShare]::ReadWrite) 
if ($stream) { 
    $reader = New-Object IO.StreamReader $stream 
    if ($reader) { 
     while(-not ($reader.EndOfStream)) { [void]$reader.ReadLine(); $count++ } 
     $reader.Close() 
    } 
    $stream.Close() 
} 

AddResult "StreamReader.ReadLine" $count 

$results | Select Method, Count, "Elapsed Time", "Memory Total", "Memory Delta" | ft -auto | Write-Output 

Dưới đây là kết quả cho tập tin văn bản có chứa ~ dòng 95K, 104 MB:

Method         Count Elapsed Time  Memory Total Memory Delta 
------         ----- ------------  ------------ ------------ 
Get-Content -ReadCount 1     95365 00:00:11.1451841   45.8   0.2 
Get-Content -ReadCount 10     95365 00:00:02.9015023   47.3   1.7 
Get-Content -ReadCount 100    95365 00:00:01.4522507   59.9   14.3 
Get-Content -ReadCount 1000    95365 00:00:01.1539634   75.4   29.7 
Get-Content -ReadCount 0     95365 00:00:01.3888746   346  300.4 
Get-Content -ReadCount 1 | Measure-Object 95365 00:00:08.6867159   46.2   0.6 
Get-Content.Count       95365 00:00:03.0574433  465.8  420.1 
StreamReader.ReadLine      95365 00:00:02.5740262   46.2   0.6 

Dưới đây là kết quả cho một tập tin lớn hơn (có chứa ~ dòng 285k, 308 MB):

Method         Count Elapsed Time  Memory Total Memory Delta 
------         ----- ------------  ------------ ------------ 
Get-Content -ReadCount 1     285776 00:00:36.2280995   46.3   0.8 
Get-Content -ReadCount 10     285776 00:00:06.3486006   46.3   0.7 
Get-Content -ReadCount 100    285776 00:00:03.1590055   55.1   9.5 
Get-Content -ReadCount 1000    285776 00:00:02.8381262   88.1   42.4 
Get-Content -ReadCount 0     285776 00:00:29.4240734  894.5  848.8 
Get-Content -ReadCount 1 | Measure-Object 285776 00:00:32.7905971   46.5   0.9 
Get-Content.Count       285776 00:00:28.4504388  1219.8  1174.2 
StreamReader.ReadLine      285776 00:00:20.4495721   46   0.4 
4

Dưới đây là một lớp lót dựa trên bài đăng của Pseudothink. Một tập tin cụ thể:

"the_name_of_your_file.txt" |% {$n = $_; $c = 0; Get-Content -Path $_ -ReadCount 1000 |% { $c += $_.Count }; "$n; $c"} 

Tất cả các file trong thư mục hiện hành:

Get-ChildItem "." |% {$n = $_; $c = 0; Get-Content -Path $_ -ReadCount 1000 |% { $c += $_.Count }; "$n; $c"} 
+0

Vui lòng giải thích chi tiết hơn. –

+0

Lê Giải pháp hoàn hảo. –

Các vấn đề liên quan