【问题标题】:Aligning the corrupted data records in a text file using powershell使用 powershell 对齐文本文件中损坏的数据记录
【发布时间】:2014-09-15 02:57:29
【问题描述】:

我的数据文件 (.txt) 有 31 个字段/列的记录,每个字段是用竖线分隔的。不知何故,很少有记录被损坏(记录被分成多行)。

任何人都可以指导编写一个脚本来读取这个输入数据文件并将其形成一个包含每条记录中正好 31 个字段的文件吗?

PS:我是 powershell 新手。

样本数据:

良好的数据 - 整条记录显示在一行中。

错误数据 - 记录被分成多行。

以下是记录的结构。

11/16/2007||0007327|       3904|1000|M1||CCM|12/31/2009|000|East 89th Street|01CM1|    11073|DONALD INC|001|Project 077|14481623.8100|0.0000|1.00000|1|EA|September 2007 Invoice|Project 027||000000000000|1330|11/16/2007|X||11/29/2007|2144.57

这是我尝试过的,但脚本挂起

#Setup paths 
$Input = "Path\Input.txt" 
$Output = "Path\Output.txt" 
#Create empty variables to set types 
$Record="" 
$Collection = @() 
#Loop through text file 
gc Path\Input.txt | %{ 
    $Record = "$Record$_" 
    If($Record -Match "(\d{1,2}/\d{1,2}/\d{4}(?:\|.*?){31})(\d{1,2}/\d{1,2}/\d{4}\|.*?\|.*)"){ 
        $Collection+=$Matches[1] 
        $Record=$Matches[2]  
    } 
} 
#Add last record to the collection 
$Collection+=$Record $Collection | Out-File $Output

【问题讨论】:

  • 请举例说明 1) 损坏情况是什么样的(缺少字段?记录分成多行?) 2) 您尝试解决的问题你自己的。
  • 这似乎很熟悉。您是否尝试将 SSMS 中的 SQL 查询结果保存到 CSV?其中一个字段中的换行符会破坏 csv 文件。
  • @FrodeF。不,我只是在处理 .txt 文件。
  • $Record=$Matches[2] 行似乎没有做任何事情。当循环以"$Record$_" 重新启动时,您将覆盖其内容
  • 这部分正则表达式似乎是一个问题(我可能错了)。 (?:\|.*?){31} 我读这篇文章是因为您在寻找 | 后跟 0 个或更多不贪婪的字符 31 次。有没有可能你的意思是(?:\|).{31}。你想在那里完成什么。 regex101.com/r/qY1jZ7/1 是测试正则表达式字符串的好资源。看来您也有不必要的反斜杠

标签: powershell


【解决方案1】:

我看到一些需要澄清或解决的问题。首先,我注意到$Record=$Matches[2] 这行似乎没有用。其次,您的正则表达式字符串似乎有一些您正在寻找的缺陷。当我在这里根据您的测试数据测试您的正则表达式时:http://regex101.com/r/yA9tZ1/1

至少在那个站点上,正斜杠需要被转义。一旦我逃脱,测试人员就会向我抛出错误

Your expression took too long to evaluate.

我知道这个问题的根源来自您的正则表达式的这一部分,它试图将您的被动组与非贪婪量词匹配 31 次。 (?:\|.*?){31}

所以猜测你的真实意图我有以下正则表达式字符串

(\d{1,2}\/\d{1,2}\/\d{4}.{31}).*?(\d{1,2}\/\d{1,2}\/\d{4}\|.*?\|.*)

您可以在此处查看结果:http://regex101.com/r/qY1jZ7/2

虽然我怀疑这是否正是您想要的,但我希望这会引导您朝着正确的方向前进。

【讨论】:

  • 他运行的代码是an answer I gave,与上周的一个非常相似的问题。 $Record=$Matches[2] 确实用于将 $Record 重置为新记录的开头,一旦 a 找到整个先前的记录。不过,该问题的记录只有 11 个字段,这对于重复被动组来说可能效果更好。
  • 现在我想起来了,我提供的 RegEx 肯定会对给定的样本数据起作用。它不应该,它不是设计来的。它设计的目的是找到并拆分两条连接的记录。要查看它是否有效,请删除第二个捕获组(第二个记录的开头是什么)。或者粘贴样本两次,不换行。然后它会正常工作。
  • 感谢@themadTechnician 指出$record 的重要性
【解决方案2】:

我刚刚尝试过,虽然该解决方案适用于一个极其相似的问题,即用户每条记录只有 11 个字段,但显然它对您的 31 个字段记录没有好处。我想建议使用-Split 以及几个正则表达式匹配的替代方案。我认为这对你来说应该更快。

#Create regex objects to match against
[RegEx]$Regex = "(.*?)(\d{2}/\d{2}/\d{4})$"
[RegEx]$Regex2 = "(\d{2}/\d{2}/\d{4}.*)"

#Setup paths 
$Input = "Path\Input.txt" 
$Output = "Path\Output.txt" 

#Create empty variables to set types 
$Record="" 
$Collection = @() 

#Loop through text file 
gc $Input | %{ 

    If($_ -match "^\d{1,2}/\d{1,2}/\d{4}" -and $record.split("|").count -eq 31){$collection+=$record;$record=$_}
    else{
        $record="$record$_"
        if($record.split("|").count -gt 31){
            $collection+=$regex.matches(($record.split("|")[0..30]) -join "|").groups[1].value
            $record=$regex2.matches(($record.split("|")[30..($record.split("|").count)]) -join "|").groups[1].value
        }
    }
}

#Add last record to the collection 
$collection+=$record

#Output everything to a file
$collection|out-file $Output

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-04-28
    • 1970-01-01
    • 2018-08-17
    • 1970-01-01
    • 2021-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多