L'une des façons d'obtenir le nombre de lignes d'un fichier est cette méthode dans PowerShell:
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>
Cependant, lorsque j'ai un gros fichier texte de 800 Mo, comment puis-je obtenir le numéro de ligne sans lire le fichier entier?
La méthode ci-dessus consommera trop RAM entraînant le plantage du script ou trop de temps pour terminer).
Utilisation Get-Content -Read $nLinesAtTime
pour lire votre fichier partie par partie:
$nlines = 0;
# Read file by 1000 lines at a time
gc $YOURFILE -read 1000 | % { $nlines += $_.Length };
[string]::Format("{0} has {1} lines", $YOURFILE, $nlines)
Et voici un script simple mais lent pour valider le travail sur un petit fichier:
gc $YOURFILE | Measure-Object -Line
Voici un script PowerShell que j'ai bricolé ensemble qui montre quelques méthodes différentes de comptage des lignes dans un fichier texte, ainsi que le temps et la mémoire requis pour chaque méthode. Les résultats (ci-dessous) montrent des différences claires dans les exigences de temps et de mémoire. Pour mes tests, il semble que l'endroit idéal était Get-Content, en utilisant un paramètre ReadCount de 100. Les autres tests ont nécessité beaucoup plus de temps et/ou d'utilisation de la mémoire.
#$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
Voici les résultats pour un fichier texte contenant ~ 95k lignes, 104 Mo:
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
Voici les résultats d'un fichier plus volumineux (contenant environ 285 000 lignes, 308 Mo):
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
Voici un one-liner basé sur le post de Pseudothink.
Lignes dans un fichier spécifique:
"the_name_of_your_file.txt" |% {$n = $_; $c = 0; Get-Content -Path $_ -ReadCount 1000 |% { $c += $_.Count }; "$n; $c"}
Tous les fichiers dans le répertoire courant (individuellement):
Get-ChildItem "." |% {$n = $_; $c = 0; Get-Content -Path $_ -ReadCount 1000 |% { $c += $_.Count }; "$n; $c"}
Explication:
"the_name_of_your_file.txt"
-> ne fait rien, fournit simplement le nom du fichier pour les étapes suivantes, doit être entre guillemets|%
-> alias ForEach-Object, itère sur les éléments fournis (un seul dans ce cas), accepte le contenu redirigé en entrée, l'élément actuel enregistré dans $_
$n = $_
-> $ n comme nom du fichier fourni est enregistré pour plus tard à partir de $_
, en fait, cela peut ne pas être nécessaire $c = 0
-> initialisation de $c
comme nombreGet-Content -Path $_ -ReadCount 1000
-> lire 1000 lignes du fichier fourni (voir les autres réponses du fil)|%
-> foreach ajoute le nombre de lignes réellement lues à $c
(sera comme 1000 + 1000 + 123)"$n; $c"
-> une fois la lecture du fichier terminée, imprimer le nom du fichier; nombre de lignes Get-ChildItem "."
-> ajoute simplement plus d'éléments au tube que le nom de fichier unique
La première chose à essayer est de diffuser Get-Content
et construire le nombre de lignes un par un, plutôt que de stocker toutes les lignes dans un tableau à la fois. Je pense que cela donnera un comportement de streaming approprié - c'est-à-dire que le fichier entier ne sera pas en mémoire à la fois, juste la ligne actuelle.
$lines = 0
Get-Content .\File.txt |%{ $lines++ }
Et comme le suggère l'autre réponse, en ajoutant -ReadCount
pourrait accélérer cela.
Si cela ne fonctionne pas pour vous (trop lent ou trop de mémoire), vous pouvez aller directement à un 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.
Voici une autre solution qui utilise .NET:
[Linq.Enumerable]::Count([System.IO.File]::ReadLines("FileToCount.txt"))
Ce n'est pas très interruptible, mais c'est très facile à mémoriser.
Voici quelque chose que j'ai écrit pour essayer de réduire l'utilisation de la mémoire lors de l'analyse de l'espace blanc dans mon fichier txt. Cela dit, l'utilisation de la mémoire est toujours élevée, mais le processus prend moins de temps à s'exécuter.
Juste pour vous donner un aperçu de mon fichier, le fichier avait plus de 2 millions d'enregistrements et comportait un espace blanc à l'avant et à l'arrière de chaque ligne. Je crois que le temps total était de 5+ minutes.
$testing = 'C:\Users\something\something\test3.txt'
$filecleanup = Get-ChildItem $testing
foreach ($file in $filecleanup)
{
$file1 = Get-Content $file -readcount 1000 | foreach{$_.Trim()}
$file1 > $filecleanup
}