如何为制表符分隔文件的无表头列赋值并实现列交换
解决无表头列识别与列交换问题
首先,我们先明确你的需求:源文件前几列是有明确表头的(比如Barcode1、Plate #、日期列),末尾是重复的无表头列组(比如示例里的A 1 4 5 6重复多次),需要识别这些无表头列并完成指定列的交换。
核心思路
- 区分有表头/无表头列范围:通过你已经实现的
dataHeaders列表长度,确定有表头列的总数,剩下的就是无表头列。 - 利用固定格式定位无表头列:因为源文件格式固定,我们可以预先定义无表头列组的大小(比如示例中每组是5列:
A 1 4 5 6),然后通过「组索引+组内列索引」来定位具体的无表头列。 - 扩展列索引获取逻辑:支持同时获取表头列和无表头列的索引。
修改后的完整代码
# 定义要交换的列:支持表头列(type=header)和无表头列(type=no-header) $swapColumns = @{ column1 = @{ type = "header"; name = "date-header"; instance = 1 } column2 = @{ type = "no-header"; groupIndex = 1; columnInGroup = 0; groupSize = 5 } # 解释:第2个无表头组(索引从0开始)的第1列(索引从0开始),每组5列 } $formats = @( 'XR-{0:yyyyMMdd}-01.txt' ) $date = [datetime]::now $ErrorActionPreference = 'Stop' # 获取表头列的索引 function Get-HeaderIndex { param( [System.Collections.Generic.List[string]]$Source, [string]$Header, [uint16]$Instance ) $index = 0; for ($i = 0; $i -lt $Instance; $i++) { $index = $Source.IndexOf($Header, $index, ($Source.Count - $index)) if (($index -eq -1) -or (($i + 1) -eq $Instance)) { break } $index = $index + 1 } if ($index -eq -1) { throw "表头 '$Header' 的第 $Instance 次出现未找到" } return $index } # 获取无表头列的索引 function Get-NoHeaderColumnIndex { param( [int]$HeaderColumnCount, # 有表头列的总数 [int]$GroupIndex, # 无表头组的索引(从0开始) [int]$ColumnInGroup, # 组内列的索引(从0开始) [int]$GroupSize # 每个无表头组的列数 ) $calculatedIndex = $HeaderColumnCount + ($GroupIndex * $GroupSize) + $ColumnInGroup return $calculatedIndex } # 获取目标文件 $fileDetails = Get-ChildItem $PSScriptRoot\UCX-*.txt | Select-Object -First 1 $file = Get-Content $fileDetails # 解析文件中的各个 section $sections = @() for ($i = 0; $i -lt $file.Count; $i++) { if ($file[$i] -match '^=+(\w+)=+$') { $sections += [pscustomobject]@{line = $i; header = $Matches[1]} } } # 获取data section和下一个section的位置 $dataSection = $sections | Where-Object {$_.header -eq 'data'} $nextSection = $sections | Where-Object {$_.line -gt $dataSection.line} | Sort-Object -Property line | Select-Object -First 1 # 解析data表头,将日期格式的表头统一标记为'date-header' $dataHeaders = New-Object System.Collections.Generic.List[string] $file[$dataSection.line + 1].Split("`t") | ForEach-Object { $headerTrimmed = $_.Replace('EST','').Trim() $headerDateValue = [datetime]::MinValue $headerIsDate = [datetime]::TryParse($headerTrimmed, [ref]$headerDateValue) if ($headerIsDate) { $dataHeaders.Add('date-header') } else { $dataHeaders.Add($headerTrimmed) } } # 获取要交换的两个列的索引 # 处理第一列 if ($swapColumns.column1.type -eq "header") { $column1 = Get-HeaderIndex -Source $dataHeaders -Header $swapColumns.column1.name -Instance $swapColumns.column1.instance } else { $column1 = Get-NoHeaderColumnIndex -HeaderColumnCount $dataHeaders.Count ` -GroupIndex $swapColumns.column1.groupIndex ` -ColumnInGroup $swapColumns.column1.columnInGroup ` -GroupSize $swapColumns.column1.groupSize } # 处理第二列(修正了原代码中缺失$符号的错误) if ($swapColumns.column2.type -eq "header") { $column2 = Get-HeaderIndex -Source $dataHeaders -Header $swapColumns.column2.name -Instance $swapColumns.column2.instance } else { $column2 = Get-NoHeaderColumnIndex -HeaderColumnCount $dataHeaders.Count ` -GroupIndex $swapColumns.column2.groupIndex ` -ColumnInGroup $swapColumns.column2.columnInGroup ` -GroupSize $swapColumns.column2.groupSize } # 遍历数据行,交换指定列 for ($i = $dataSection.line + 2; $i -lt $nextSection.line - 1; $i++) { $line = $file[$i] $parts = $line.Split("`t") # 如果有空列可以加参数:[System.StringSplitOptions]::RemoveEmptyEntries # 交换列数据 $temp = $parts[$column1] $parts[$column1] = $parts[$column2] $parts[$column2] = $temp $file[$i] = $parts -join "`t" } # 输出新文件 $formats | ForEach-Object { $file | Out-File ($_ -f $date) -Force -Encoding utf8 # 指定编码避免乱码问题 }
关键修改说明
- 扩展
swapColumns定义:新增type字段区分表头列和无表头列,无表头列需要指定groupIndex(组序号)、columnInGroup(组内列序号)和groupSize(每组列数)。 - 新增
Get-NoHeaderColumnIndex函数:根据有表头列总数、组索引和组内列索引计算无表头列的具体位置。 - 修正原代码错误:原代码中
Get-HeaderIndex -Header swapColumns.column2.name缺失$符号,已修正为$swapColumns.column2.name。 - 优化细节:添加了错误提示信息、指定文件编码避免乱码问题。
使用注意事项
- 根据你的实际文件调整
$swapColumns中的参数,特别是无表头列的groupSize(必须和源文件中每组无表头列的列数一致)。 - 索引均从0开始计数,比如
groupIndex=1代表第2个无表头组。
内容的提问来源于stack exchange,提问作者Johnnyc
相关产品推荐
相关产品推荐

