【问题标题】:Powershell - Count number of carriage returns line feed in .txt filePowershell - 计算.txt文件中的回车换行数
【发布时间】:2021-01-20 16:20:07
【问题描述】:

我有一个大文本文件(从 SQL db 输出),我需要确定行数。但是,由于源 SQL 数据本身包含回车符 \r 和换行符 \n(从不一起出现),因此某些行的数据跨越输出 .txt 文件中的多行。我在下面使用的 Powershell 为我提供了大于实际 SQL 行数的文件行数。所以我需要修改脚本以忽略额外的行 - 一种方法可能只是计算文件中 CRLF\r\n 出现(TOGETHER)的次数,这应该是实际的行数,但是我不知道该怎么做。

Get-ChildItem "." |% {$n = $_; $c = 0; Get-Content -Path $_ -ReadCount 1000 |% { $c += $_.Count }; "$n; $c"} > row_count.txt

【问题讨论】:

  • [regex]::Matches((Get-Content file.txt -Raw),'\r\n').Count 之类的东西应该可以工作。
  • @AdminOfThings 谢谢!我现在就试试这个
  • 这很好用。唯一的问题是我得到了内存不足的异常,因为文件是 12gb。有没有办法避免这种情况?也许就像一次读几千行?

标签: powershell


【解决方案1】:

我刚刚了解到Get-Content 通过CRCRLFLF 拆分和流式传输文件中的每一行,以便它可以在操作系统之间互换读取数据:

"1`r2`n3`r`n4" | Out-File .\Test.txt
(Get-Content .\Test.txt).Count
4

再次阅读问题,我可能误解了您的问题。
在任何情况下,如果您只想对特定字符组合进行拆分(计数):

CR

((Get-Content -Raw .\Test.txt).Trim() -Split '\r').Count
3

LF

((Get-Content -Raw .\Test.txt).Trim() -Split '\n').Count
3

CRLF

((Get-Content -Raw .\Test.txt).Trim() -Split '\r\n').Count # or: -Split [Environment]::NewLine
2

注意.Trim() 方法,它删除了Get-Content -Raw 参数添加的文件末尾的额外换行符(空格)。


附录

(根据内存异常的评论更新)
恐怕目前没有其他选择,然后使用ReadBlock 方法构建自己的StreamReader 并专门在CRLF 上拆分行。我已经为此问题打开了功能请求:-NewLine Parameter to customize line separator for Get-Content

获取线

解决内存异常错误的可能方法:

function Get-Lines {
    [CmdletBinding()][OutputType([string])] param(
        [Parameter(ValueFromPipeLine = $True)][string] $Filename,
        [String] $NewLine = [Environment]::NewLine
    )
    Begin {
        [Char[]] $Buffer = new-object Char[] 10
        $Reader = New-Object -TypeName System.IO.StreamReader -ArgumentList (Get-Item($Filename))
        $Rest = '' # Note that a multiple character newline (as CRLF) could be split at the end of the buffer
    }
    Process {
       While ($True) {
            $Length = $Reader.ReadBlock($Buffer, 0, $Buffer.Length)
            if (!$length) { Break }
            $Split = ($Rest + [string]::new($Buffer[0..($Length - 1)])) -Split $NewLine
            If ($Split.Count -gt 1) { $Split[0..($Split.Count - 2)] }
            $Rest = $Split[-1]
        }
    }
    End {
        $Rest
    }
}

用法

为防止内存异常,请勿将结果分配给变量或使用方括号,因为这将停止 PowerShell PowerShell 管道并将所有内容存储在内存中。

$Count = 0
Get-Lines .\Test.txt | ForEach-Object { $Count++ }
$Count

【讨论】:

  • 非常感谢!我试过((Get-Content -Raw .\Test.txt).Trim() -Split '\r\n').Count。唯一的问题是我得到一个内存不足的异常,因为文件是 12gb。有没有办法避免这种情况?也许就像一次读几千行?
  • 感谢@iRon!我会支持这个请求!
  • System.IO.StreamReader 解决方案上做得很好;请注意,您可以使用[string]::new($Buffer[0..($Length - 1)]) 加快速度,这比一元-join 快得多。至于纯PowerShell解决方案:注意Get-Content有一个-Delimiter参数,这里可以使用(见我的回答)。
  • 太好了,感谢@iRon!。我首先在一个较小的文件上对其进行了测试,看起来它确实比您之前的答案花费了稍长的时间,但正确地产生了计数!我现在将在更大的文件上尝试一下。
  • 返回的字符串中不应有任何CRLF。请注意,该函数返回一个字符串流(集合)(string[])。无论如何,@mklement0 的解决方案可能更好(至少更容易)。我完全错过了Get-Content cmdlet 的-Delimiter 参数。
【解决方案2】:
  • 以固定大小块读取文件并在iRon's helpful answer 中执行自定义拆分为行的System.IO.StreamReader.ReadBlock 解决方案是最佳选择,因为它既避免了内存不足问题又表现良好(按PowerShell 标准) .

  • 如果执行速度方面的性能不是最重要的,您可以利用
    Get-Content-Delimiter 参数,它接受自定义字符串来拆分文件内容作者:

# Outputs the count of CRLF-terminated lines.
(Get-Content largeFile.txt -Delimiter "`r`n" | Measure-Object).Count

请注意,-Delimiter 在拆分时采用可选终止符逻辑:也就是说,如果文件内容给定的分隔符字符串结尾, no 额外,最后报空元素。

这与默认行为一致,其中文件中的尾随换行符被视为可选终止符,导致报告额外的空行。

但是,如果使用了与换行符无关的-Delimiter 字符串,则尾随换行符视为最终“行”(元素)。

一个简单的例子:

# Create a test file without a trailing newline.
# Note the CR-only newline (`r) after 'line 1'
"line1`rrest of line1`r`nline2" | Set-Content -NoNewLine test1.txt

# Create another test file with the same content plus 
# a trailing CRLF newline.
"line1`rrest of line1`r`nline2`r`n" | Set-Content -NoNewLine test2.txt

'test1.txt', 'test2.txt' | ForEach-Object {
  "--- $_"
  # Split by CRLF only and enclose the resulting lines in [...]
  Get-Content $_ -Delimiter "`r`n" | 
    ForEach-Object { "[{0}]" -f ($_ -replace "`r", '`r') }
}

这会产生:

--- test1.txt
[line1`rrest of line1]
[line2]
--- test2.txt
[line1`rrest of line1]
[line2]

如您所见,两个测试文件的处理方式相同,因为尾随的 CRLF 换行符被认为是最后一行的可选终止符。

【讨论】:

  • 谢谢!但这会遇到内存问题吗?
  • 如果最后一行实际上不包含CRLF,则此方法有时会生成<NumberOfCRLF>+1
  • @Chipmunk_da:不,该命令使用管道的流式传输行为,因此一次读取一行。如果您有单独的 CRLF 行本身就是数 GB,那么您只会遇到麻烦,这似乎不太可能。
  • @AdminOfThings:该命令计算的数量,而不是CRLF序列的数量,因为尾随的CRLF序列被认为是可选的终止符 ,而不是 分隔符 - 请查看我的更新。
猜你喜欢
  • 1970-01-01
  • 2019-02-06
  • 1970-01-01
  • 2019-07-21
  • 2012-09-26
  • 1970-01-01
  • 1970-01-01
  • 2011-10-14
  • 2018-08-10
相关资源
最近更新 更多