2012-08-23 10 views
14

Eine der Möglichkeiten, um nicht zu bekommen. aus einer Datei von Linien ist diese Methode in PowerPowershell bekommen Anzahl der Zeilen der großen (großen) Datei

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> 

Allerdings, wenn ich eine große 800 MB Textdatei habe, wie bekomme ich die Zeilennummer aus, ohne die gesamte Datei zu lesen?

Die obige Methode wird zu viel RAM verbrauchen, was zu einem Absturz des Skripts führen oder zu lange dauern wird.

Antwort

13

Verwenden Get-Content -Read $nLinesAtTime Dateien Teil

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

von Teil zu lesen Und hier ist einfach, aber langsam Skript Arbeit auf kleine Datei

gc $YOURFILE | Measure-Object -Line 
+1

Es ist erwähnenswert, dass Ihr zweiter Ansatz nur die Zeilen mit Text zählt. Wenn es leere Zeilen gibt, werden sie nicht gezählt. – Vladislav

8

Das erste, was ist zu versuchen, zu validieren Get-Content zu streamen und Erstellen Sie die Zeilenzählung nacheinander, anstatt alle Zeilen in einem Array gleichzeitig zu speichern. Ich denke, dass dies ein korrektes Streaming-Verhalten ergibt - d. H. Die gesamte Datei wird nicht auf einmal gespeichert, sondern nur die aktuelle Zeile.

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

Und wie die andere Antwort schon sagt, -ReadCount Aufsummierung dies beschleunigen könnte.

Wenn das nicht für Sie arbeiten (zu langsam oder zu viel Speicher) direkt an einen StreamReader gehen könnte:

$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

Die Verwendung des obigen IO.StreamReader-Codes hat die Fehler im Arbeitsspeicher behoben, die bei Verwendung der folgenden GC-Methode aufgetreten sind. Ich kann bestätigen, dass es viel weniger Speicher verbraucht (mit PowerShell 5.0.10514.6) – Fares

1

Hier ist etwas, was ich zu versuchen, den Speicherverbrauch verringert wird geschrieben, wenn das Parsen aus Leerzeichen in meiner TXT-Datei. Damit wird die Speicherauslastung immer noch etwas hoch, aber der Prozess benötigt weniger Zeit zum Ausführen. Nur um Ihnen einen Hintergrund meiner Datei zu geben, hatte die Datei mehr als 2 Millionen Datensätze und einen führenden Leerraum sowohl vorne als auch hinten in jeder Zeile. Ich glaube, die Gesamtzeit war 5+ Minuten Bitte lassen Sie mich Ihre Gedanken wissen, wenn es eine Möglichkeit gibt, die Formatierung zu verbessern. dank

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

$filecleanup = gci $testing 

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

Hier ist ein Powershell-Skript ich zusammengeschustert, die ein paar verschiedene Methoden des Zählens Zeilen in einer Textdatei zeigt, zusammen mit der Zeit und Speicher für jedes Verfahren erforderlich. Die Ergebnisse (unten) zeigen deutliche Unterschiede in den Zeit- und Speicheranforderungen. Für meine Tests sah es so aus, als ob der Sweetspot Get-Content mit einer ReadCount-Einstellung von 100 war. Die anderen Tests erforderten wesentlich mehr Zeit und/oder Speicherverbrauch.

#$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 

Hier sind Ergebnisse für Textdatei mit ~ 95k Zeilen, 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 

Hier sind Ergebnisse für eine größere Datei (mit ~ 285K Zeilen, 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

Hier ist ein Einzeiler basierend auf Pseudothinks Post. Eine bestimmte Datei:

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

Alle Dateien im aktuellen Verzeichnis:

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

Bitte erläutern Sie es ausführlicher. –

+0

Le Perfekte Lösung. –