2017-10-03 11 views
1

私はファイルごとに行単位で余分な区切り文字を探しています。 しかし、ファイル内のヘッダー行(最初の行)とフッター行(最後の行)を無視して、ファイルの詳細に焦点を当てたいと思います。Powershell - ファイル内のヘッダー行(最初の行)とフッター行(最後の行)を無視するのが難しい

ReadLine()メソッドを使用して最初と最後の行を無視する方法がわかりません。私はファイルを何らかの方法で変更したくないので、このスクリプトは、余分なデリミタを持つCSVファイルの行を識別するためにのみ使用されます。

注意:検索するファイルには何百万行もあり、そのためにはGet-Contentの手法ではなくReadLine()メソッドに頼らざるを得ません。

私はSelect-Object -Skip 1 | Select-Object -SkipLast 1$measureに値を入力するステートメントに使用しようとしましたが、希望の結果が得られませんでした。例えば

H|Transaction|2017-10-03 12:00:00|Vendor --> This is the Header 
D|918a39230a098134|2017-08-31 00:00:00.000|2017-08-15 00:00:00.000|SLICK-2340|... 
D|918g39230b095134|2017-08-31 00:00:00.000|2017-08-15 00:00:00.000|EX|SRE-68|... 
T|1268698 Records --> This is Footer 

は基本的に、私は私のスクリプトは、ヘッダとフッタを無視したい、となるように正しいレコードの例及び他の詳細レコードとして最初のデータ行(D|918...)を使用しエラーのためにそれと比較(この例では第二の詳細行は、返さなければならないので、そこに無効なデリミタフィールド(EX|SRE-68...)。

Iはget-content文で-skip 1-skiplast 1を使用してみました、プロセスがまだありますヘッダー行を使用してすべての詳細レコードを無効なレコードとして戻します。

はここに...私がこれまで持っているものだ

編集者注:明記の意図にもかかわらず、このコードは、参照列数を決定するために ヘッダー行(1行目)を使用して行います。

$File = "test.csv" 
$Delimiter = "|" 

$measure = Get-Content -Path $File | Measure-Object 
$lines = $measure.Count 

Write-Host "$File has ${lines} rows." 

$i = 1 

$reader = [System.IO.File]::OpenText($File) 
$line = $reader.ReadLine() 
$reader.Close() 
$header = $line.Split($Delimiter).Count 

$reader = [System.IO.File]::OpenText($File) 
try 
{ 
    for() 
    { 
     $line = $reader.ReadLine() 
     if($line -eq $null) { break } 
     $c = $line.Split($Delimiter).Count 
     if($c -ne $header -and $i -ne${lines}) 
     { 
      Write-Host "$File - Line $i has $c fields, but it should be $header" 
     } 
     $i++ 
    } 
} 

finally 
{ 
    $reader.Close() 
} 

答えて

0

は、私たちはここに大きな入力ファイルを読み込む(Get-Contentへの高速の代替として)のみ[System.IO.TextFile].ReadLine()を使用するソリューションだ、とだけたらそう、パフォーマンスがを重要ことを知っていること:

  • Get-Content ... | Measure-Object介して行数のいいえアップフロントカウント、

  • ヘッダー行を読み込むためだけにファイルを開くインスタンスはありません。ヘッダー行を読み込んだ後にファイルを開いたままにしておけば、読み続けるだけの利点があります(ヘッダー行をスキップする必要はありません)。


$File = "test.csv" 
$Delimiter = "|" 

# Open the CSV file as a text file for line-based reading. 
$reader = [System.IO.File]::OpenText($File) 

# Read the lines. 
try { 

    # Read the header line and discard it. 
    $null = $reader.ReadLine() 

    # Read the first data line - the reference line - and count its columns. 
    $refColCount = $reader.ReadLine().Split($Delimiter).Count 

    # Read the remaining lines in a loop, skipping the final line. 
    $i = 2 # initialize the line number to 2, given that we've already read the header and the first data line. 
    while ($null -ne ($line = $reader.ReadLine())) { # $null indicates EOF 

    ++$i # increment line number 

    # If we're now at EOF, we've just read the last line - the footer - 
    # which we want to ignore, so we exit the loop here. 
    if ($reader.EndOfStream) { break } 

    # Count this line's columns and warn, if the count differs from the 
    # header line's. 
    if (($colCount = $line.Split($Delimiter).Count) -ne $refColCount) { 
     Write-Warning "$File - Line $i has $colCount fields rather than the expected $refColCount." 
    } 

    } 

} finally { 

    $reader.Close() 

} 
1

Read Lineを使用する理由は何ですか?あなたのやっているGet-ContentはすでにCSV全体をメモリに読み込んでいますので、変数に保存してからループを使って最初の行をスキップします。

したがって、このような何か:

$File = "test.csv" 
$Delimiter = "|" 

$contents = Get-Content -Path $File 
$lines = $contents.Count 

Write-Host "$File has ${lines} rows." 

$header = $contents[0].Split($Delimiter).count 

for ($i = 1; $i -lt ($lines - 1); $i++) 
{ 
    $c = $contents[$i].Split($Delimiter).Count 
    if($c -ne $header) 
    { 
     Write-Host "$File - Line $i has $c fields, but it should be $header" 
    } 
} 
+1

あなたはあなたの助けthe_swとmklement0ありがとうございました! 私が検索しようとしているファイルは、何百万行にもなりますが、get-contentメソッドを使用すると、あまりうまく機能しないようです。たとえば、get-contentメソッドを使用して、500,000行の大きなファイルの場合、約30分かかります。 – Pavan

0

注:この答えはOPは、パフォーマンスが重要であることが明らかとなっ前に書かれたGet-Contentベースのソリューションは、オプションはそのためではなかったという。私のother answerはそれに対応しました。
この答えはまだ遅くなりますが、より簡潔な、PowerShellの-慣用的なソリューションのために重要である可能性があります。

the_sw's helpful answerはあなたの.NET Frameworkの直接使用に頼る必要がなく、便利なファイルを読むためのPowerShell独自のGet-Contentコマンドレットを使用することができることを示しています。

PSV5 +は、より簡潔でよりメモリ効率的である慣用単一のパイプライン溶液を可能にする - それは行を一つずつ処理 - ものの性能を犠牲にし。特に大きなファイルの場合は、一度にすべてのファイルを読み取る必要がない場合があるため、パイプラインの解決策が望ましい

パラメータを使用するため、PSv5 +が必要です。今

$File = "test.csv" 
$Delimiter = '|' 

Get-Content $File | Select-Object -SkipLast 1 | ForEach-Object { $i = 0 } { 
    if (++$i -eq 1) { 
    return # ignore the actual header row 
    } elseif ($i -eq 2) { # reference row 
    $refColumnCount = $_.Split($Delimiter).Count 
    } else { # remaining rows, except the footer, thanks to -SkipLast 1 
    $columnCount = $_.Split($Delimiter).Count 
    if ($columnCount -ne $refColumnCount) { 
     "$File - Line $i has $columnCount fields rather than the expected $refColumnCount." 
    } 
    } 
} 
関連する問題