2013-03-13 97 views
3

所有的,我對powershell都很陌生,希望有人能讓我繼續做我認爲是簡單的腳本。Powershell解析文本文件的一部分並保存爲CSV

我需要解析一個文本文件,從它捕獲某些行,並將這些行保存爲csv文件。

例如,每個警報都在它自己的文本文件中。每個文件與此類似:

文件--start ---

名稱約翰·史密斯
部門會計
代碼BAS-2349,CAV-3928,DEG-3942
           耶-2830,TEL-3890
的url hxxp://blah.com
        hxxp://foo.com,hxxp:// foo2的.COM
一些文字我不關心
更多的文字我不關心
評論
---------
「在這裏是一個多線
評論我需要
捕獲」
一些文字我不關心
更多的文字我不關心
日期2013年3月12日

---文件結尾---

對於每個文本文件,如果我只想將名稱,代碼和Url寫入CSV文件。有人能幫我解決這個問題嗎?

我更喜歡PERL,所以我知道我可以寫一個正則表達式來捕獲以Name開頭的單行。然而,我完全迷失在如何閱讀「代碼」這一行時,它可能是一行,或者它可能是X行,直到我遇到Urls字段。

任何幫助將不勝感激!

+0

多少數據你找誰處理。 PS可能不是最好的選擇,除非你受到其他限制。 [本答案](http://stackoverflow.com/a/4192419/326543)討論關於PS文本處理的perf基準 – 2013-03-13 03:38:50

回答

0

文本解析通常意味着正則表達式。有了正則表達式,有時候你需要錨點來知道什麼時候停止比賽,並且可以讓你關心那些你不知道的文本。如果您可以指定「我不關心的某些文本」的第一行,則可以使用它來「錨定」您的URL匹配,以便知道何時停止匹配。

$regex = @' 
(?ms)Name (.+)? 
Dept .+? 
Codes (.+)? 
Urls (.+)? 
Some text I dont care about.+ 
Comments 
--------- 
(.+)? 
Some text I dont care about 
'@ 

$file = 'c:\somedir\somefile.txt' 
[IO.File]::ReadAllText($file) -match $regex 
if ([IO.File]::ReadAllText($file) -match $regex) 
    { 
    $Name = $matches[1] 
    $Codes = $matches[2] -replace '\s+',',' 
    $Urls = $matches[3] -replace '\s+',',' 
    $comment = $matches[4] -replace '\s+',' ' 
    } 

$Name 
$Codes 
$Urls 
$comment 
+0

該OP特別要求持續線路的幫助。 – 2013-03-13 13:44:51

+0

文件讀取方法不正確(現在已更正)。除此之外,這是一個多行的正則表達式,即它用於匹配和捕獲來自續行的數據。 – mjolinor 2013-03-13 23:14:25

+0

我看到了,您在問題中假設關鍵字會在行首,爲問題的字面文本量身定製了正則表達式。 OP可能必須澄清這一點。然而,你的正則表達式依賴於OP所說的他不關心的文本知識。該文本可能因文件而異,這將是一個問題。 – 2013-03-14 08:12:03

0

如果文件不是太大而無法在內存中處理,最簡單的方法是將其作爲字符串數組讀取。 (對於你的系統來說,太大的意思是什麼,任何小於千兆字節的東西都應該工作而沒有太多的連接。)

讀完文件後,設置一個頭部和尾部計數器指向元素零。向前移動尾指針,直到找到日期行。您可以將數據與正則表達式匹配。現在你知道單個記錄的開始和結束。對於下一條記錄,將頭部計數器設置爲尾部+ 1,尾部到尾部+ 2並再次開始掃描行。起泡,沖洗,重複直至排列結束。

當記錄匹配時,您可以使用正則表達式提取名稱。代碼和Url有點棘手。將代碼行與正則表達式匹配。提取它和所有下一行,除非它們不匹配代碼模式。 Urls數據也一樣。如果文件的行數是前一個URL和代碼的數據行中的空白填充,那麼可以使用匹配空白計數和正則表達式來獲取數據行。

0

也許一些行這將爲它:

foreach ($Line in gc file.txt) { 
    switch -regex ($Line) { 
     '^(Name|Dept|Codes|Urls)' { 
      $Capture = $true 
      break 
     } 
     '^[A-Za-z0-9_-]+' { 
      $Capture = $false 
      break 
     } 
    } 
    if ($Capture) { 
     $Line 
    } 
} 

如果你想作爲一個CSV文件,那麼你可以使用Export-Csv cmdlet的最終結果。

0

如果所有文件具有相同的結構,你可以做這樣的事情:

$srcdir = "C:\Test" 
$outfile = "$srcdir\out.csv" 

$re = '^Name (.*(?:\r\n .*)*)\r\n' + 
     'Dept .*(?:\r\n .*)*\r\n' + 
     'Codes (.*(?:\r\n .*)*)\r\n' + 
     'Urls (.*(?:\r\n .*)*)' + 
     '[\s\S]*$' 

Get-ChildItem $srcdir -Filter *.txt | % { 
    [io.file]::ReadAllText($_.FullName) 
} | Select-String $re | % { 
    $f = $_.Matches | % { $_.Groups } | ? { $_.Index -gt 0 } 
    New-Object -TypeName PSObject -Prop @{ 
     'Name' = $f[0].Value; 
     'Codes' = $f[1].Value; 
     'Urls' = $f[2].Value; 
    } 
} | Export-Csv $outfile -NoTypeInformation 
0

根據該c:\temp\file.txt包含一個事實:

Name John Smith 
Dept Accounting 
Codes bas-2349,cav-3928,deg-3942 
     iye-2830,tel-3890 
Urls hxxp://blah.com 
    hxxp://foo.com 
    hxxp://foo2.com 
Some text I dont care about 
More text i dont care about 
. 
. 
Date 3/12/2013 

您可以使用正則表達式是這樣的:

$a = Get-Content C:\temp\file.txt 
$b = [regex]::match($a, "^.*Codes (.*)Urls (.*)Some.*$", "Multiline") 
$codes = $b.groups[1].value -replace '[ ]{2,}',',' 
$urls = $b.groups[2].value -replace '[ ]{2,}',','