在 PowerShell 中使用 [Linq.Enumerable] 获取最后一次出现的特定字符串的行号
Get the line number of last occurrence of a particular string with [Linq.Enumerable] in PowerShell
有没有办法获取文件中最后一次出现的行索引,而不是行本身?
$delegate = [Func[string,bool]] {$args[0] -match $myString}
$lastCheckIn = [Linq.Enumerable]::Last([System.IO.File]::ReadLines($myFile), $delegate)
我想过使用 [Linq.Enumerable]::Count
,但找不到从头到 $lastCheckIn
的序列 return 的方法。
限制Linq的方法,可以试试下面的方法
$lines = 'aaa','bbb','ccc','ddd','aaa','bbb','ccc','ddd'
$searchPattern = 'c+'
$selectDelegate = [Func[object, int32, object]] { @{line=$args[0]; index=$args[1] } }
$whereDelegate = [Func[object,bool]] { $args[0].line -match $searchPattern }
$objects = [Linq.Enumerable]::Select($lines, $selectDelegate)
$lastObject = [Linq.Enumerable]::Last($objects, $whereDelegate)
$lastObject.index
# result = 6 (zero-based index)
# Without Linq
$lines | Select-String $searchPattern | Select-Object -Last 1 -ExpandProperty LineNumber
# result = 7 (one-based index)
这首先构建了一个 line/index 对的集合,过滤最后一个匹配项,然后提取索引。我还包含了一个 non-linq 等效的 powershell。
我相信这应该更快更容易实施、维护和理解:
$pattern = 'yourpatternhere'
$content = [System.IO.File]::ReadAllLines('path/to/file.ext')
$tail = $content.Count
while($tail--)
{
if($content[$tail] -match $pattern)
{
"$pattern was found on line: $tail"
break
}
}
显示了有效的 LINQ 解决方案以及 PowerShell-idiomatic 替代方案。
提高性能有两种选择:
- 如果可以先将整个文件加载到内存中,请使用
[Array]::FindLastIndex()
(Get-Content -ReadCount 0
本质上等同于 [System.IO.File]::ReadAllLines()
的 PowerShell):
[Array]::FindLastIndex(
(Get-Content -ReadCount 0 $myFile), # -ReadCount 0 returns all lines as single array
[Predicate[string]] { $args[0] -match $myString }
)
- 一个优化的 LINQ 解决方案,带有惰性枚举:
$i = $index = -1
$null = [Linq.Enumerable]::LastOrDefault(
[IO.File]::ReadLines($myFile),
[Func[string, bool]] {
++$script:i;
if ($args[0] -match $myString) { $script:index = $script:i; return $true }
}
)
$index # output the index of the last match, if not found, -1
警告:这种查找索引的方法仅适用于 lazy 枚举作为输入,因为只有它们需要 向前枚举直到最后一个元素。
相比之下,list-like 可枚举项(实现 System.Collections.IList
接口或其通用对应项的那些)被枚举 向后,来自列表的 end,以优化性能。
类似地,如果您希望最后一个匹配行接近(r) 到大文件的end,您需要延迟读取文件backwards 以获得最佳性能,没有标准的 .NET API。以处理 variable-width 字符编码(例如 UTF-8)的方式这样做是非常重要的,但是 - 请参阅 this answer.
有没有办法获取文件中最后一次出现的行索引,而不是行本身?
$delegate = [Func[string,bool]] {$args[0] -match $myString}
$lastCheckIn = [Linq.Enumerable]::Last([System.IO.File]::ReadLines($myFile), $delegate)
我想过使用 [Linq.Enumerable]::Count
,但找不到从头到 $lastCheckIn
的序列 return 的方法。
限制Linq的方法,可以试试下面的方法
$lines = 'aaa','bbb','ccc','ddd','aaa','bbb','ccc','ddd'
$searchPattern = 'c+'
$selectDelegate = [Func[object, int32, object]] { @{line=$args[0]; index=$args[1] } }
$whereDelegate = [Func[object,bool]] { $args[0].line -match $searchPattern }
$objects = [Linq.Enumerable]::Select($lines, $selectDelegate)
$lastObject = [Linq.Enumerable]::Last($objects, $whereDelegate)
$lastObject.index
# result = 6 (zero-based index)
# Without Linq
$lines | Select-String $searchPattern | Select-Object -Last 1 -ExpandProperty LineNumber
# result = 7 (one-based index)
这首先构建了一个 line/index 对的集合,过滤最后一个匹配项,然后提取索引。我还包含了一个 non-linq 等效的 powershell。
我相信这应该更快更容易实施、维护和理解:
$pattern = 'yourpatternhere'
$content = [System.IO.File]::ReadAllLines('path/to/file.ext')
$tail = $content.Count
while($tail--)
{
if($content[$tail] -match $pattern)
{
"$pattern was found on line: $tail"
break
}
}
提高性能有两种选择:
- 如果可以先将整个文件加载到内存中,请使用
[Array]::FindLastIndex()
(Get-Content -ReadCount 0
本质上等同于[System.IO.File]::ReadAllLines()
的 PowerShell):
[Array]::FindLastIndex(
(Get-Content -ReadCount 0 $myFile), # -ReadCount 0 returns all lines as single array
[Predicate[string]] { $args[0] -match $myString }
)
- 一个优化的 LINQ 解决方案,带有惰性枚举:
$i = $index = -1
$null = [Linq.Enumerable]::LastOrDefault(
[IO.File]::ReadLines($myFile),
[Func[string, bool]] {
++$script:i;
if ($args[0] -match $myString) { $script:index = $script:i; return $true }
}
)
$index # output the index of the last match, if not found, -1
警告:这种查找索引的方法仅适用于 lazy 枚举作为输入,因为只有它们需要 向前枚举直到最后一个元素。
相比之下,list-like 可枚举项(实现 System.Collections.IList
接口或其通用对应项的那些)被枚举 向后,来自列表的 end,以优化性能。
类似地,如果您希望最后一个匹配行接近(r) 到大文件的end,您需要延迟读取文件backwards 以获得最佳性能,没有标准的 .NET API。以处理 variable-width 字符编码(例如 UTF-8)的方式这样做是非常重要的,但是 - 请参阅 this answer.