大文本文件的匹配操作速度问题

问题描述 投票:1回答:3

我有36个.log-Files的数据基础,我需要预先处理它们以便将它们加载到pandas数据框中以便在python框架内进行数据可视化。

要提供其中一个.log-Files中单行的示例:

[16:24:42]: Downloaded 0 Z_SYSTEM_FM traces from DEH, clients (282) from 00:00:00,000 to 00:00:00,000 

从这里的几个来源和帖子我发现以下代码是表现最好的代码:

foreach ($f in $files){

    $date = $f.BaseName.Substring(22,8)

    ((Get-Content $f) -match "^.*\bDownloaded\b.*$") -replace "[[]", "" -replace "]:\s", " " 
    -replace "Downloaded " -replace "Traces from " -replace ",.*" -replace "$", " $date" 
    | add-content CleanedLogs.txt

}

变量$date包含日期,相应的.log文件正在记录。

我无法更改输入文本数据。我尝试使用-raw读取1,55GB,但在处理完所有操作后,我无法分割得到的单个字符串。另外,我尝试使用更多的正则表达式,但总运行时没有减少。也许有一种方法可以使用grep进行此操作?

也许某人有天才的调整来加速这个操作。此时此操作需要近20分钟才能计算出来。非常感谢你!

powershell powershell-v3.0
3个回答
1
投票

提高绩效的关键是:

  • 避免使用管道和cmdlet,特别是文件I / O(Get-ContentAdd-Content) 请改用System.IO.File类型的方法。
  • 避免在PowerShell代码中循环。 相反,链数组感知运算符,如-match-replace - 你已经在做了。 合并你的正则表达式以减少-replace调用。 使用预编译的正则表达式。

把它们放在一起:

# Create precompiled regexes.
# Note: As written, they make the matching that -replace performs
#       case-*sensitive* (and culture-sensitive), 
#       which speeds things up slightly.
#       If you need case-*insensitive* matching, use option argument
#       'Compiled, IgnoreCase' instead.
$reMatch    = New-Object regex '\bDownloaded\b', 'Compiled'
$reReplace1 = New-Object regex 'Downloaded |Traces from |\[', 'Compiled'
$reReplace2 = New-Object regex '\]:\s', 'Compiled'
$reReplace3 = New-Object regex ',.*', 'Compiled'

# The platform-appropriate newline sequence.
$nl = [Environment]::NewLine

foreach ($f in $files) {

  $date = $f.BaseName.Substring(22,8)

  # Read all lines into an array, filter and replace, then join the
  # resulting lines with newlines and append the resulting single string
  # to the log file.
  [IO.File]::AppendAllText($PWD.ProviderPath + '/CleanedLogs.txt',
    ([IO.File]::ReadAllLines($f.FullName) -match
      $reMatch -replace 
        $reReplace1 -replace 
          $reReplace2, ' ' -replace 
            $reReplace3, " $date" -join 
              $nl) + $nl
  )

}

请注意,每个文件必须作为一个行数组整合到内存中,加上它的一部分(作为数组和单个多行字符串),其大小取决于过滤的行数。


1
投票

我过去也遇到过类似的问题。简而言之,在使用大型文件时,直接使用.NET会更快。您可以通过阅读performance considerations了解更多信息。

最快的方法可能是使用IO.FileStream。例如:

$File = "C:\Path_To_File\Logs.txt"
$FileToSave = "C:\Path_To_File\result.txt"
$Stream = New-Object -TypeName IO.FileStream -ArgumentList ($File), ([System.IO.FileMode]::Open), ([System.IO.FileAccess]::Read), ([System.IO.FileShare]::ReadWrite)
$Reader = New-Object -TypeName System.IO.StreamReader -ArgumentList ($Stream, [System.Text.Encoding]::ASCII, $true)
$Writer = New-Object -TypeName System.IO.StreamWriter -ArgumentList ($FileToSave)
while (!$Reader.EndOfStream)
{
    $Box = $Reader.ReadLine()
    if($Box -match "^.*\bDownloaded\b.*$")
    {
        $ReplaceLine = $Box -replace "1", "1234" -replace "[[]", ""
        $Writer.WriteLine($ReplaceLine)
    }
}
$Reader.Close()
$Writer.Close()
$Stream.Close()

您应该可以非常轻松地编辑上面的代码以满足您的需求。要获取文件列表,您可以使用Get-ChildItem

另外我建议你阅读this stackoverflow帖子。


0
投票

也许这会为你加快速度:

$outFile = Join-Path -Path $PSScriptRoot -ChildPath 'CleanedLogs.txt'
$files   = Get-ChildItem -Path '<YOUR ROOTFOLDER>' -Filter '*.txt' -File
foreach ($f in $files){
    $date = $f.BaseName.Substring(22,8)
    [string[]]$lines = ([System.IO.File]::ReadAllLines($f.FullName) | Where-Object {$_ -match '^.*\bDownloaded\b.*$'} | ForEach-Object {
        ($_ -replace '\[|Downloaded|Traces from|,.*', '' -replace ']:\s', ' ' -replace '\s+', ' ') + " $date"
    })
    [System.IO.File]::AppendAllLines($outFile, $lines)
}
© www.soinside.com 2019 - 2024. All rights reserved.