如何在 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}
我有一个大文本文件 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}