【问题标题】:Powershell parse parts of a text file and save to CSVPowershell 解析部分文本文件并保存为 CSV
【发布时间】:2013-02-28 20:00:05
【问题描述】:

所有,我对 powershell 很陌生,希望有人可以让我继续我认为是一个简单的脚本。

我需要解析一个文本文件,从中捕获某些行,并将这些行保存为 csv 文件。

例如,每个警报都在其自己的文本文件中。每个文件都类似这样:

--文件开始---

姓名约翰·史密斯
会计部
代码 bas-2349,cav-3928,deg-3942
iye-2830,电话 3890
网址 hxxp://blah.com
hxxp://foo.com, hxxp://foo2.com
一些我不关心的文字
更多我不关心的文字
评论
---------
“这是一个多行
评论我需要
捕捉”
一些我不关心的文字
更多我不关心的文字
日期 2013 年 3 月 12 日

---文件结束---

对于每个文本文件,如果我只想将名称、代码和 URL 写入 CSV 文件。有人可以帮我做这件事吗?

我更喜欢 PERL,所以我知道我可以编写一个正则表达式来捕获以 Name 开头的单行。但是,我完全不知道如何阅读“代码”行,因为它可能是一行,或者可能是 X 行,直到我遇到 Urls 字段。

任何帮助将不胜感激!

【问题讨论】:

  • 您要处理多少数据。 PS 可能不是最佳选择,除非您受到其他限制。 This answer 谈 PS 文本处理性能基准测试

标签: parsing powershell text csv


【解决方案1】:

文本解析通常意味着正则表达式。使用正则表达式,有时您需要锚知道何时停止匹配,这可以让您关心您原本不会关心的文本。如果您可以指定“我不关心的某些文本”的第一行,您可以使用它来“锚定”您的 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

【讨论】:

  • OP 专门寻求帮助以解决续行问题。
  • 文件读取方法不正确(现已更正)。除此之外,这是一个多行正则表达式 - 即它用于匹配和捕获连续行中的数据。
  • 我明白了,当我假设关键字位于行首时,您将正则表达式调整为问题中的文字文本。 OP可能必须澄清这一点。但是,您的正则表达式依赖于 OP 表示他不关心的文本知识。该文本可能因文件而异,这将是一个问题。
  • 正则表达式很容易调整间距,如果它不完全一样。 OP 不关心后面的文本,因为它不包含他想要捕获的数据。在 OP 的带领下,我以提供的文字文本为例。只要它是可预测的替换实际文本以匹配就可以了。如果该文本不可预测,这只是一个问题。考虑到周围文字的上下文,我认为这不太可能。
  • mjolinor,我喜欢你的方向。但是我无法让该代码输出任何内容。我已经使用我上面发布的测试数据将它指向我本地文件系统上的一个文件。它可以很好地读取文件,因为如果我给它一个无效的文件名,powershell 会向我抱怨。但是它运行但不显示任何内容。我把它放在一个以 .ps1 结尾的文件中,并使用 .\my-script.ps1 通过 powershell 执行我做错了什么?
【解决方案2】:

如果文件不是太大而无法在内存中处理,简单的方法是将其作为字符串数组读取。 (太大意味着什么取决于您的系统。任何低于千兆字节的内容都应该可以正常工作。)

读取文件后,设置头尾计数器以指向元素零。将尾指针逐行向前移动,直到找到日期行。您可以将数据与正则表达式匹配。现在您知道了单个记录的开始和结束。对于下一条记录,将 head counter 设置为 tail+1,tail 设置为 tail+2,然后再次开始扫描行。起泡、冲洗、重复直到到达阵列末端。

当记录匹配时,您可以使用正则表达式提取名称。代码和网址有点棘手。将代码行与正则表达式匹配。提取它和所有接下来的行,除非它们与代码模式不匹配。 Urls 数据也是如此。如果文件总是在作为先前 URL 和代码的数据的行上使用空格填充,则您也可以使用 match whitespace count 和正则表达式来获取数据行。

【讨论】:

    【解决方案3】:

    也许这会对它产生影响:

    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。

    【讨论】:

      【解决方案4】:

      根据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,}',','
      

      【讨论】:

        【解决方案5】:

        如果所有文件都具有相同的结构,您可以这样做:

        $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
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-11-07
          • 2017-05-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-02-16
          • 2011-12-23
          • 1970-01-01
          相关资源
          最近更新 更多