[CmdletBinding()] param( [Parameter()] [string]$SourcePath = (Join-Path $PSScriptRoot '..\app\src'), [Parameter()] [switch]$ShowFiles ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' function Get-CodeLineStatistics { param( [Parameter(Mandatory)] [string]$FilePath ) $statistics = [ordered]@{ TotalLines = 0 BlankLines = 0 CommentOnlyLines = 0 PreprocessorLines = 0 CodeLines = 0 } $inBlockComment = $false $inRawString = $false $rawStringTerminator = '' foreach ($line in [System.IO.File]::ReadLines($FilePath, [System.Text.UTF8Encoding]::new($false))) { $statistics.TotalLines++ $index = 0 $code = [System.Text.StringBuilder]::new() $containsComment = $inBlockComment while ($index -lt $line.Length) { if ($inRawString) { $terminatorIndex = $line.IndexOf($rawStringTerminator, $index, [System.StringComparison]::Ordinal) if ($terminatorIndex -lt 0) { [void]$code.Append($line.Substring($index)) $index = $line.Length continue } [void]$code.Append($line.Substring($index, $terminatorIndex + $rawStringTerminator.Length - $index)) $index = $terminatorIndex + $rawStringTerminator.Length $inRawString = $false continue } if ($inBlockComment) { $containsComment = $true $commentEnd = $line.IndexOf('*/', $index, [System.StringComparison]::Ordinal) if ($commentEnd -lt 0) { $index = $line.Length continue } $index = $commentEnd + 2 $inBlockComment = $false continue } $current = $line[$index] $next = if ($index + 1 -lt $line.Length) { $line[$index + 1] } else { [char]0 } if ($current -eq '/' -and $next -eq '/') { $containsComment = $true break } if ($current -eq '/' -and $next -eq '*') { $containsComment = $true $inBlockComment = $true $index += 2 continue } if ($current -eq 'R' -and $next -eq '"') { $delimiterStart = $index + 2 $openParenthesis = $line.IndexOf('(', $delimiterStart) if ($openParenthesis -ge 0 -and $openParenthesis - $delimiterStart -le 16) { $delimiter = $line.Substring($delimiterStart, $openParenthesis - $delimiterStart) if ($delimiter -notmatch '[\s\\()]') { $rawStringTerminator = ')' + $delimiter + '"' [void]$code.Append($line.Substring($index, $openParenthesis - $index + 1)) $index = $openParenthesis + 1 $inRawString = $true continue } } } if ($current -eq '"' -or $current -eq "'") { $quote = $current [void]$code.Append($quote) $index++ while ($index -lt $line.Length) { $stringCharacter = $line[$index] [void]$code.Append($stringCharacter) $index++ if ($stringCharacter -eq '\\' -and $index -lt $line.Length) { [void]$code.Append($line[$index]) $index++ continue } if ($stringCharacter -eq $quote) { break } } continue } [void]$code.Append($current) $index++ } $remaining = $code.ToString().Trim() if ([string]::IsNullOrWhiteSpace($line)) { $statistics.BlankLines++ } elseif ($remaining.StartsWith('#')) { $statistics.PreprocessorLines++ } elseif (-not [string]::IsNullOrWhiteSpace($remaining)) { $statistics.CodeLines++ } elseif ($containsComment) { $statistics.CommentOnlyLines++ } else { $statistics.BlankLines++ } } return [pscustomobject]$statistics } $resolvedSourcePath = (Resolve-Path -LiteralPath $SourcePath).Path $files = Get-ChildItem -LiteralPath $resolvedSourcePath -Recurse -File -Filter '*.cpp' | Sort-Object FullName if ($files.Count -eq 0) { throw "未在 '$resolvedSourcePath' 找到 .cpp 文件" } $fileResults = foreach ($file in $files) { $statistics = Get-CodeLineStatistics -FilePath $file.FullName [pscustomobject]@{ File = $file.FullName.Substring($resolvedSourcePath.Length).TrimStart('\', '/') CodeLines = $statistics.CodeLines TotalLines = $statistics.TotalLines BlankLines = $statistics.BlankLines CommentOnlyLines = $statistics.CommentOnlyLines PreprocessorLines = $statistics.PreprocessorLines } } $total = [pscustomobject]@{ File = '合计' CodeLines = [int]($fileResults | Measure-Object -Property CodeLines -Sum).Sum TotalLines = [int]($fileResults | Measure-Object -Property TotalLines -Sum).Sum BlankLines = [int]($fileResults | Measure-Object -Property BlankLines -Sum).Sum CommentOnlyLines = [int]($fileResults | Measure-Object -Property CommentOnlyLines -Sum).Sum PreprocessorLines = [int]($fileResults | Measure-Object -Property PreprocessorLines -Sum).Sum } Write-Host "扫描目录: $resolvedSourcePath" Write-Host "统计范围: $($files.Count) 个 .cpp 文件,不统计 .h;移除注释及 # 预处理指令后统计代码行" Write-Host "总行数: $($total.TotalLines)" Write-Host "纯 C++ 代码: $($total.CodeLines) 行" Write-Host "空白行: $($total.BlankLines) | 纯注释行: $($total.CommentOnlyLines) | 预处理行: $($total.PreprocessorLines)" if ($ShowFiles) { $fileResults | Format-Table File, CodeLines, TotalLines, BlankLines, CommentOnlyLines, PreprocessorLines -AutoSize }