TessellatingHeckler's helpful answer 包含一个实用的、易于理解的解决方案,在实践中很可能足够快; Robert Cotterman's helpful answer 也是如此,它简洁(而且速度也更快)。
如果性能真的很重要,您可以尝试以下方法,它直接使用 .NET 框架来读取行 - 但是给出你只需要阅读 2 行,这可能不值得:
$inputFile = "$PWD/some.csv" # be sure to specify a *full* path
$isFirstLine=$true
$fname = foreach ($line in [IO.File]::ReadLines($inputFile)) {
if ($isFirstLine) { $isFirstLine = $false; continue } # skip header line
$line -replace '^([^,]*),.*', '$1' # extract 1st field from 2nd line and exit
break # exit
}
注意:提取第一个字段的概念上更简单的方法是使用($line -split ',')[0],但对于大量列,上述基于-replace 的方法明显更快。
更新:TessellatingHeckler 提供了 2 种方法来加快上述速度:
使用$line.Substring(0, $line.IndexOf(',')) 代替$line -replace '^([^,]*),.*', '$1' 以避免相对昂贵的regex 处理。
为了减少收益,请连续两次使用[System.IO.StreamReader] 实例的.ReadLine() 方法,而不是循环使用[IO.File]::ReadLines()。
这是此页面上所有答案的方法的性能比较(截至撰写本文时)。
要自己运行,必须先下载函数New-CsvSampleData和Time-Command。
为了获得更具代表性的结果,时间是 1000 次运行的平均值:
# Create sample CSV file 'test.csv' with 850 columns and 100 rows.
$testFileName = "test-$PID.csv"
New-CsvSampleData -Columns 850 -Count 100 | Set-Content $testFileName
# Compare the execution speed of the various approaches:
Time-Command -Count 1000 {
# Import-Csv
Import-Csv -LiteralPath $testFileName |
Select-Object -Skip 1 -First 1 -ExpandProperty 'col1'
}, {
# ReadLines(), -replace
$inputFile = $PWD.ProviderPath + "/$testFileName"
$isFirstLine=$true
foreach ($line in [IO.File]::ReadLines($inputFile)) {
if ($isFirstLine) { $isFirstLine = $false; continue } # skip header line
$line -replace '^([^,]*),.*', '$1' # extract 1st field from 2nd line and exit
break # exit
}
}, {
# ReadLines(), .Substring / IndexOf
$inputFile = $PWD.ProviderPath + "/$testFileName"
$isFirstLine=$true
foreach ($line in [IO.File]::ReadLines($inputFile)) {
if ($isFirstLine) { $isFirstLine = $false; continue } # skip header line
$line.Substring(0, $line.IndexOf(',')) # extract 1st field from 2nd line and exit
break # exit
}
}, {
# ReadLine() x 2, .Substring / IndexOf
$inputFile = $PWD.ProviderPath + "/$testFileName"
$f = [System.IO.StreamReader]::new($inputFile,$true);
$null = $f.ReadLine(); $line = $f.ReadLine()
$line.Substring(0, $line.IndexOf(','))
$f.Close()
}, {
# Get-Content -Head, .Split()
((Get-Content $testFileName -Head 2)[1]).split(',')[1]
} |
Format-Table Factor, Timespan, Command
Remove-Item $testFileName
在最近型号的 MacBook Pro 上运行 Windows PowerShell v5.1 / PowerShell Core 6.1.0-preview.4 的单核 Windows 10 VM 的示例输出:
Windows PowerShell v5.1:
Factor TimeSpan Command
------ -------- -------
1.00 00:00:00.0001922 # ReadLine() x 2, .Substring / IndexOf...
1.04 00:00:00.0002004 # ReadLines(), .Substring / IndexOf...
1.57 00:00:00.0003024 # ReadLines(), -replace...
3.25 00:00:00.0006245 # Get-Content -Head, .Split()...
25.83 00:00:00.0049661 # Import-Csv...
PowerShell Core 6.1.0-preview.4:
Factor TimeSpan Command
------ -------- -------
1.00 00:00:00.0001858 # ReadLine() x 2, .Substring / IndexOf...
1.03 00:00:00.0001911 # ReadLines(), .Substring / IndexOf...
1.60 00:00:00.0002977 # ReadLines(), -replace...
3.30 00:00:00.0006132 # Get-Content -Head, .Split()...
27.54 00:00:00.0051174 # Import-Csv...
结论:
调用.ReadLine() 两次比::ReadLines() 循环稍快。
使用 -replace 代替 Substring() / IndexOf() 会增加大约 60% 的执行时间。
使用Get-Content会慢3倍以上。
使用Import-Csv | Select-Object会慢近30倍(!),大概是由于列数很大;也就是说,从绝对意义上讲,我们仍然只谈论大约 5 毫秒。
附带说明:总体而言,macOS 上的执行似乎明显变慢,正则表达式解决方案和 cmdlet 调用也相对较慢。