使用获取内容或导入CSV来读取csv中第二行的第一列

时间:2018-08-07 23:53:39

标签: powershell powershell-v4.0

所以我有一个25MB的CSV文件。 我只需要获取存储在第一列第二行中的值,并稍后在powershell脚本中使用它即可。

例如数据

File_name,INVNUM,ID,XXX....850 columns
ABCD,123,090,xxxx.....850 columns
ABCD,120,091,xxxx.....850 columns
xxxxxx5000+ rows

所以我的第一列数据始终是相同的,我只需要从第一列第二行获取此文件名。

在这种情况下,我应该尝试使用Get-content还是Import-csv吗?

谢谢, 米奇

3 个答案:

答案 0 :(得分:2)

取决于您要确定的优先级。

$data = Import-Csv -LiteralPath 'c:\temp\data.csv' | 
            Select-Object -Skip 1 -First 1 -ExpandProperty 'File_Name'

简短方便。 (第二行表示文件的第二行或数据的第二行?如果它是数据的第一行,则不要跳过任何内容。)

具有-First 1之类的Select-Object完成后会中断整个管道,因此它不会等待在返回之前在后台读取25MB的其余部分。

如果您打开文件,查找两个换行符,一个逗号,然后读到另一个逗号或一些其他较长的详细代码,则可能会加快速度或减少内存使用,数量不多非常值得怀疑。

Get-Content一样,它将NoteProperties添加到输出字符串的方式将意味着它在内存上可能不会比Import-Csv更加容易,并且使用速度也不会比'ay'

答案 1 :(得分:2)

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提供了两种加快上述速度的方法:

  • 使用$line.Substring(0, $line.IndexOf(','))代替$line -replace '^([^,]*),.*', '$1',以避免相对昂贵的 regex 处理。

  • 要获得较小的收益,请连续两次使用[System.IO.StreamReader]实例的.ReadLine()方法,而不要循环使用[IO.File]::ReadLines()

这是此页上(截至撰写本文时)所有答案的方法的性能比较

要自己运行它,必须先下载功能New-CsvSampleDataTime-Command

要获得更具代表性的结果,请在1,000次运行中平均计时:

# 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上,执行速度似乎明显较慢,而regex解决方案和cmdlet调用相对而言也较慢。

答案 2 :(得分:1)

您真的可以用

来缩短它
(gc c:\file.txt -head 2)[1]

仅读取2行,然后获取索引1(第二行)

然后可以将其拆分。并获取分割线的索引1

((gc c:\file.txt -head 2)[1]).split(',')[1]

UPDATE :::经常看到new post之后,我被启发自己做一些测试(感谢mklement0)。这是我上班最快的时间

$check = 0
foreach ($i in [IO.FILE]::ReadLines("$filePath")){
    if ($check -eq 2){break}
    if ($check -eq 1){$value = $i.split(',')[1]}  #$value = your answer
    $check++
}

只需考虑一下:删除-eq 2并在执行检查1后将break放在半冒号后面。加快5个滴答声。还没测试。

这是我超过40000个测试的结果:

  • GC拆分平均值为1.11307622毫秒
  • GC拆分最小值为0.3076毫秒
  • GC分裂最大值为18.1514毫秒
  • ReadLines拆分平均值为0.3836625825毫秒
  • ReadLines分割最小值为0.2309毫秒
  • ReadLines拆分最大值为31.7407毫秒
  • 流读取器平均avg为0.4464924825毫秒
  • 流读取器的MIN为0.2703毫秒
  • Stream Reader Max为31.4991毫秒
  • 导入CSV的平均值为1.32440485毫秒
  • 导入CSV MIN为0.2875毫秒
  • 导入CSV最大为103.1694毫秒

我能够在第二和第三次上每秒进行3000次测试,并且在第一个和最后一次上每秒进行1000次测试。 Stream Reader 是HIS最快的一款。导入CSV还不错,我想知道mklement0的测试csv中是否没有名为“ file_name”的列?无论如何,我个人会使用GC命令,因为它简洁明了且易于记忆。但这取决于您,我希望您在脚本冒险中走运。

我敢肯定,我们可以开始对此进行超线程处理并获得疯狂的结果,但是当您谈论千分之一秒时,这真的很重要吗?特别是要获得一个变量? :D

这是我出于透明原因使用的流阅读器代码...

$inputFile = "$filePath"
$f = [System.IO.StreamReader]::new($inputFile,$true); 
$null = $f.ReadLine(); $line = $f.ReadLine()
$line.Substring(0, $line.IndexOf(','))
$f.Close()

我还注意到这拉动第二行的第一个值,而且我不知道如何将其切换为第二个值...似乎正在测量从点0到第一个逗号的宽度,然后切割那。如果您将子字符串从0更改为5,则它仍将0的长度改为逗号,但是将开始抓取的位置移动到了第六个字符。

我使用的import-csv是:

$data = Import-Csv -LiteralPath "$filePath" | 
        Select-Object -Skip 1 -First 1 -ExpandProperty 'FileName'

我在90兆的csv上测试了这些结果,具有21列和284k行。而“ FileName”是第二列