Format-BakNRetPaddedText:按**列宽**补齐或裁剪,且绝不切开宽字符 —— 剩余宽度差 1 列而下一个字符要占 2 列时,停下用空格补那 1 列。返回的字符串保证恰好 N 列,这条不变式是画边框的基础(按 .Length 补会让边框歪,按 .Length 裁会把汉字劈成半个显示成乱码)。 Write-BakNRetAt:第一道闸门是 [Console]::IsOutputRedirected,**不是** $Host.UI.SupportsVirtualTerminal(实测后者无控制台时仍返回 True,而 SetCursorPosition 会抛异常)。没有控制台就完全不定位、只把文本写出去;坐标越界时跳过定位但仍写出文本 —— 计划任务与窄窗口不该让 TUI 崩掉,只是画得难看一点。颜色走 Write-Host -ForegroundColor(零 VT 依赖)。 验收:断言 10 条(含"裁剪后恰好 5 列且不是 4 个字符"这条关键判据);两版手工输出一致;test.ps1 9/9 全绿。
50 lines
1.7 KiB
PowerShell
50 lines
1.7 KiB
PowerShell
function Format-BakNRetPaddedText {
|
||
<#
|
||
.SYNOPSIS
|
||
把文本按**列宽**补齐或裁剪到指定宽度。
|
||
|
||
.DESCRIPTION
|
||
为什么不能用 `.PadRight()` / `.Substring()`:中文 1 个 char 占 2 列。按字符数补,边框会歪;
|
||
按字符数裁,会把一个汉字劈成半个、显示成乱码。
|
||
|
||
这里一律按 Get-BakNRetCellWidth 算,并且**绝不切开一个宽字符** —— 当剩余宽度差 1 列而下一个
|
||
字符要占 2 列时,就停下并用空格补上那 1 列。返回的字符串保证**恰好 Width 列**(Width ≤ 0 时
|
||
返回空串),这条不变式是画边框与对齐的基础。
|
||
#>
|
||
param(
|
||
[Parameter(Mandatory = $true)][AllowEmptyString()][string]$Text,
|
||
[Parameter(Mandatory = $true)][int]$Width
|
||
)
|
||
|
||
if ($Width -le 0) { return '' }
|
||
|
||
$textWidth = Get-BakNRetCellWidth -Text $Text
|
||
if ($textWidth -le $Width) {
|
||
return $Text + (' ' * ($Width - $textWidth))
|
||
}
|
||
|
||
# 超宽:按列裁剪,绝不切开代理对或宽字符
|
||
$kept = ''
|
||
$used = 0
|
||
$index = 0
|
||
while ($index -lt $Text.Length) {
|
||
$chunk = [string]$Text[$index]
|
||
$high = [int]$Text[$index]
|
||
if ($high -ge 0xD800 -and $high -le 0xDBFF -and ($index + 1) -lt $Text.Length) {
|
||
$low = [int]$Text[$index + 1]
|
||
if ($low -ge 0xDC00 -and $low -le 0xDFFF) {
|
||
$chunk = $Text.Substring($index, 2)
|
||
$index++
|
||
}
|
||
}
|
||
$index++
|
||
|
||
$chunkWidth = Get-BakNRetCellWidth -Text $chunk
|
||
if (($used + $chunkWidth) -gt $Width) { break }
|
||
$kept += $chunk
|
||
$used += $chunkWidth
|
||
}
|
||
|
||
return $kept + (' ' * ($Width - $used))
|
||
}
|