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

How to split a very large text file (4GB) at pre-defined string in powershell and do it fast

我有一个大文本文件 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]

[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 框架,以完成工作。

编辑 1:最终代码 修复了接受的答案中的一些错误,这是我使用的最终代码(它可能对任何想要编辑大型 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()

一般来说,在性能很重要的情况下,在 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("*"))

写一般需要很大的开销。
因此,将节数据保存在内存中,直到完成并且 一次写入整个节:

$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}