我已经格式化了其他来源的文本文件;我无法控制这些来源,也无法要求他们生成更适合我用途的格式,例如CSV。我可以查看文件的标题行以确定列宽(和名称,但此处不存在问题)。完成此操作后,我将获得一系列宽度。我希望能够根据我根据标头确定的宽度在该文件中拆分后续行。
很明显,我可以遍历宽度数组,并咬断适当长度的初始子字符串,但是我希望有一种更有效的方法-例如,如果我想使用固定宽度的列,只能使用-split "(\w{$foo})"
,其中$foo
是包含列宽的变量。
事实上,有没有更有效的方法?
示例数据:
Junction 0122 D150441-4 Ni Po De 210 Na
列宽$cols=@(14, 5, 11, 2, 16, 3, 4, 2)
(注意:我不在乎切碎的数据中的尾部空格;我可以稍后再管理。我只是想在此时切碎数据。)>
(在iRon要求能够证明其ConvertFrom-SourceTable
的请求下,这是一个完整的文件,可能需要解析)
@SUB-SECTOR: sec_C SECTOR: reft
#
# Trade routes within the subsector
#
#--------1---------2---------3---------4---------5---------6---
#PlanetName Loc. UPP Code B Notes Z PBG Al LRX *
#---------- ---- --------- - --------------- - --- -- --- -
Lemente 1907 B897563-B Ag Ni 824 Na
Zamoran 2108 B674675-A Q Ag Ni 904 Dr
答案 0 :(得分:1)
事实上,有没有更有效的方法?
如果用“更高效”来表示“花费更少CPU周期的事情”,那么可以:
$string = 'Junction 0122 D150441-4 Ni Po De 210 Na'
$cols = @(14, 5, 11, 2, 16, 3, 4, 2)
$substrings = @(
$cols |Select -SkipLast 1 |ForEach-Object {
$string.Remove($_)
$string = $string.Substring($_)
}
$string
)
# $substrings now contain the individual column values
上面的代码将通过从字符串的先前副本中不断删除前n-1
个子字符串来捕获它们。
如果用“更高效”来表示“更少的代码”,则可以连接构造的正则表达式模式并一次性捕获所有捕获组:
$string = 'Junction 0122 D150441-4 Ni Po De 210 Na'
$cols = @(14, 5, 11, 2, 16, 3, 4, 2)
# generate the regex pattern
# in this case '(.{14})(.{5})(.{11})(.{2})(.{16})(.{3})(.{4})(.{2})'
$pattern = $cols.ForEach({"(.{$_})"})-join''
# use `-match` and $Matches to grab the individual groups
$substrings = if($string -match $pattern){
$Matches[1..($cols.Length-1)]
}
# $substrings again holds all our substrings
答案 1 :(得分:1)
ConvertFrom-SourceTable
$Text = @'
@SUB-SECTOR: sec_C SECTOR: reft
#
# Trade routes within the subsector
#
#--------1---------2---------3---------4---------5---------6---
#PlanetName Loc. UPP Code B Notes Z PBG Al LRX *
#---------- ---- --------- - --------------- - --- -- --- -
Lemente 1907 B897563-B Ag Ni 824 Na
Zamoran 2108 B674675-A Q Ag Ni 904 Dr
'@
您可以将原始here string用作ConvertFrom-SourceTable
cmdlet的输入,但是如果使用Get-Content
从文件中检索数据,则$Table
可能是字符串(行)数组:
$Table = $Text -Split "[\r\n]+"
如果标题从未更改,则最简单的方法是使用-Header
和-Ruler
参数重新定义标题行和标尺:
$Table | Select -Skip 7 | ConvertFrom-SourceTable `
-Header 'PlanetName Loc. UPP Code B Notes Z PBG Al LRX *' `
-Ruler '---------- ---- --------- - --------------- - --- -- --- -' `
| Format-Table
PlanetName Loc. UPP Code B Notes Z PBG Al LRX *
---------- ---- -------- - ----- - --- -- --- -
Lemente 1907 B897563-B Ag Ni 824 Na
Zamoran 2108 B674675-A Q Ag Ni 904 Dr
(顺便说一下,-Ruler
参数实际上不是必需的,可以在此特定表中省略)
如果每个表的标题都不同,则可以考虑自动重新格式化表,然后从标题和标尺行中删除#
并用空格替换:
$Table | Select -Skip 5 |
ForEach-Object {$_ -Replace '^#', ' '} |
ConvertFrom-SourceTable | Format-Table
第一列未完全正确对齐,但随后的数据将使该数据更直。对此有一个例外:特别是在流 [1] 中提供输入时,与标题/标尺正确对齐的数据(通常是整数,如下面的示例所示)将为ConvertFrom-SourceTable cmdlet解释。
[1]如果将输入作为管道流(而不是此处的原始字符串)提供,则ConvertFrom-SourceTable
cmdlet将充当in the middle of a pipeline cmdlet并为下一个释放中间对象cmdlet。因此,它只能确定直到当前行的列连接和调整。
ConvertFrom-SourceTable '
PlanetName Loc. UPP Code B Notes Z PBG Al LRX *
---------- ---- -------- - ----- - --- -- --- -
12345789012 1907 B897563-B Ag Ni 824 Na
123 2108 B674675-A Q Ag Ni 904 Dr
' | Format-Table
(请注意,上表输入与Format-Table
输出完全相同)
换句话说,如果第一列第一行的字段是与标题右对齐的字符串(如上例中的12个字符),则如果无法解释该字段,则会产生错误(例如,如果不是数字)。您可以使用-Literal
开关来避免这种情况。
我猜想此命令将使用ConvertFrom-SourceTable
cmdlet来完成整个技巧:
$Table | Select -Skip 5 |
ForEach-Object {$_ -Replace '^#', ' '} |
ConvertFrom-SourceTable -Literal | Format-Table
我为ConvertFrom-SourceTable
添加了一项新功能,对于像这样的浮动表可能会出现:
-Floating
默认情况下,带有标尺的浮动表中的介绍不是 通过管道流式传输会被自动跳过。
如果为管道输入提供了-Floating
开关,则 对象流将从标尺开始(流浮表 不能没有统治力。)
如果显式禁用了浮动(-Floating:$False
),则标头 即使表没有流式传输,也假定位于第一行。
这意味着您可以简化命令,不必再-Skip
到特定行:
ConvertFrom-SourceTable -Literal ($Table | ForEach-Object {$_ -Replace '^#', ' '})
如果要从(大)输入文件中流式传输数据,则需要提供-Floating
开关以告知cmdlet等待标尺:
$Table | ForEach-Object {$_ -Replace '^#', ' '} |
ConvertFrom-SourceTable -Literal | Format-Table