代码之家  ›  专栏  ›  技术社区  ›  scobi

如何以流的形式逐行处理PowerShell中的文件

  •  86
  • scobi  · 技术社区  · 14 年前

    我正在处理一些千兆字节的文本文件,希望使用PowerShell对它们进行流处理。它很简单,只需解析每一行并提取一些数据,然后将其存储在数据库中。

    不幸的是, get-content | %{ whatever($_) } 似乎将管道的此阶段的整组线保留在内存中。它的速度也惊人的慢,需要很长时间才能真正读懂。

    所以我的问题是两部分:

    1. 我怎样才能使它逐行处理流,而不将整个东西保存在内存中?我想避免为此目的耗尽几吉格内存。
    2. 我怎样才能让它跑得更快?PowerShell迭代 get-content 似乎比C脚本慢100倍。

    我希望我在这里做些蠢事,比如错过一个 -LineBufferSize 参数什么的。。。

    3 回复  |  直到 10 年前
        1
  •  90
  •   Eduardo Pelais    8 年前

    如果您真的要处理千兆字节的文本文件,请不要使用PowerShell。即使你找到了阅读它的方法,在PowerShell中,快速处理大量的行也会很慢,而且你无法避免这一点。即使是简单的循环也很昂贵,比如说对于1000万次迭代(在您的例子中相当真实),我们有:

    # "empty" loop: takes 10 seconds
    measure-command { for($i=0; $i -lt 10000000; ++$i) {} }
    
    # "simple" job, just output: takes 20 seconds
    measure-command { for($i=0; $i -lt 10000000; ++$i) { $i } }
    
    # "more real job": 107 seconds
    measure-command { for($i=0; $i -lt 10000000; ++$i) { $i.ToString() -match '1' } }
    

    更新: 如果您仍然不害怕,请尝试使用.NET阅读器:

    $reader = [System.IO.File]::OpenText("my.log")
    try {
        for() {
            $line = $reader.ReadLine()
            if ($line -eq $null) { break }
            # process the line
            $line
        }
    }
    finally {
        $reader.Close()
    }
    

    更新2

    有一些关于可能更好/更短的代码的评论。原始代码没有问题 for 它不是伪代码。但越短(最短?)读取循环的变量是

    $reader = [System.IO.File]::OpenText("my.log")
    while($null -ne ($line = $reader.ReadLine())) {
        $line
    }
    
        2
  •  49
  •   tresf    8 年前

    System.IO.File.ReadLines() 非常适合这个场景。它返回文件的所有行,但允许您立即开始遍历这些行,这意味着它不必将所有内容存储在内存中。

    需要.NET 4.0或更高版本。

    foreach ($line in [System.IO.File]::ReadLines($filename)) {
        # do something with $line
    }
    

    http://msdn.microsoft.com/en-us/library/dd383503.aspx

        3
  •  6
  •   Chris Blydenstein    11 年前

    如果您想使用直接PowerShell,请查看下面的代码。

    $content = Get-Content C:\Users\You\Documents\test.txt
    foreach ($line in $content)
    {
        Write-Host $line
    }