如何在powershell中以预定义字符串分割非常大的文本文件(4GB)并快速完成

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

我有一个大型文本文件 World.net(这是一个 Pajek 文件,但将其视为文本),其内容为:

*Vertices 999999
    1 ""                                       0.2931    0.2107    0.5000 empty
    2 ""                                       0.2975    0.2214    0.5000
    3 ""                                       0.3083    0.2258    0.5000
    4 ""                                       0.3127    0.2406    0.5000
    5 ""                                       0.3083    0.2514    0.5000
    6 ""                                       0.3147    0.2578    0.5000
...
    999999 ""                                       0.3103    0.2622    0.5000
*Edges :2 "World contours"
    1     2 1 
    2     3 1 
    3     4 1 
    4     5 1 
    5     6 1 
    6     7 1 
...
    983725     8 1 

我想将其拆分为不同的 .txt 文件,以

开头的行

*[某事]

[Something] 应进入文件名,如 World_Vertices.txt 和 World_Edges.txt。

文件内容应该是原始文件中每个类别(顶点、边)后面的行 (1,2,3...),没有类别名称(以 * 开头)。

我有一个(有点)有效的代码:

$filename = "World"
echo $pwd\"$filename.net"
$file = New-Object System.IO.StreamReader -Arg "$pwd\$filename.net"
while (($line = $file.ReadLine()) -ne $null) {
    If ($line -match "^\*\w+") {
        $newfile = -join("$filename ","$($line.Split('\*')[1]).txt")
        echo $newfile
    }
    Else {
        $line | Out-File -Append $newfile
    }
}

但是这段代码非常慢。处理 10 MB 的文件需要 20 分钟。 我希望能够处理 4GB 文件。

硬件说明:机器很好:i7,带混合磁盘,16GB 内存,我可以安装 .net 框架,无论需要做什么。

powershell text-files large-files pajek
3个回答
2
投票

一般来说,当性能很重要时,在 PowerShell 中使用 .NET 函数始终是最佳方法。所以使用

StreamReader
已经是一个很好的方法了。

我更改了您的代码以使用

StreamWriter
写入输出文件:

$filename = "World"
echo "$pwd\$filename.net"
$file = New-Object System.IO.StreamReader -Arg "$pwd\$filename.net"
$writer = $null
while (($line = $file.ReadLine()) -ne $null) {
    If ($line -match "^\*\w+") {
        $newfile = -join("$filename ","$($line.Split('\*')[1]).txt")
        echo $newfile
        if ($null -ne $writer) {
            $writer.Dispose()
        }
        $writer = New-Object System.IO.StreamWriter "$pwd\$newfile"
    }
    Else {
        $writer.WriteLine($line)
    }
}

尝试一下。

还有其他方法可以进一步提高您的表现。例如,您可以跳过昂贵的正则表达式检查。用这个代替:

if ($line.StartsWith("*"))

1
投票

一般来说,写作需要很大的开销。
因此,请将部分数据保留在内存中,直到完成为止,然后立即写入整个部分:

$filename = "World"
echo $pwd\"$filename.net"
$file = New-Object System.IO.StreamReader -Arg "$pwd\$filename.net"
while (($line = $file.ReadLine()) -ne $null) {
    If ($line -match "^\*\w+") {
        If ($newfile) {$section | Out-File $newfile}
        $newfile = -join("$filename ","$($line.Split('\*')[1]).txt")
        echo $newfile
        $section = @()
    }
    Else {
        $Section += $line
    }
}
If ($newfile) {$section | Out-File $newfile}

0
投票

将OP的解决方案从问题迁移到答案:

修复了已接受答案中的一些错误,这是我使用的最终代码(它可能对任何想要编辑大型pajek文件的人有帮助):

$filename = "World.net"
$file = New-Object System.IO.StreamReader -Arg "$pwd\$filename"
$writer = $null
$n = 0
while (($line = $file.ReadLine()) -ne $null) {
   If ($line.StartsWith("*")) {
       $n = 1
       $newfile = -join("$filename ","$($line.Split('\*')[1]).txt")
       echo $newfile
       if ($null -ne $writer) {
           $writer.Dispose()
       }
       $writer = New-Object System.IO.StreamWriter "$pwd\$newfile"
   }
   Else {
       If ($n -eq 0){
           $writer.WriteLine()
       }
       $writer.Write($line)
       $n = 0
   }
}
$writer.Dispose()
© www.soinside.com 2019 - 2024. All rights reserved.