【问题标题】:Removing Duplicates from CSV File using PowerShell使用 PowerShell 从 CSV 文件中删除重复项
【发布时间】:2023-03-27 11:57:01
【问题描述】:

我有一个单列 CSV 文件。根据我的机器有多少故障代码,此列将有不同数量的代码(最多 10 个子列 - 请参见下面的示例)。我想操作此 CSV,以便输出是唯一故障代码的干净列表已经发生的。

示例 CSV 文件 (sample.csv):

ActiveFaults

00:1523 00:1345 00:1343 90:1344

00:1523 00:1345 00:1343 90:1344

00:1523 00:1345 00:1343 90:1344

00:1523 00:1345 00:1343 90:1344

00:1523 00:1345 00:1343 90:1344 90:5900 90:8988

00:1523 00:1345 00:1343 90:1344 90:5900 90:8988

BA:8797 BA: 1330

理想的输出是以下格式的 CSV 文件:

理想输出.csv

UniqueActiveFaults

00:1523

00:1345

00:1343

90:1344

90:5900

90:8988

BA:8797

BA:1330

任何想法如何做到这一点?我已经尝试了几种方法(使用 -Sort、-Group 等...但没有一个按预期工作)谢谢。

【问题讨论】:

    标签: powershell csv


    【解决方案1】:

    不要再将文件视为 CSV。

    只需读入一个字符串,用空格将其拆分并通过管道将其发送到Sort-Object -Unique

    $Values = (Get-Content .\sample.csv -Raw) -split '\s+' | Where-Object {$_ -like '*:*'}
    "UniqueActiveFaults" |Out-File .\IdealOutput.csv
    $Values | Sort-Object -Unique | Out-File .\IdealOutput.csv
    

    -split 运算符将正则表达式作为其右手操作数,在本例中为 \s+\s 是“空白”字符类的简写,+ 表示“匹配 1 个或多个前面的字符”

    如果文件很大,可以在第一条语句中使用ReadCount参数将处理分成块:

    Get-Content .\sample.csv -ReadCount 100 |ForEach-Object {$_ -split '\s+'}
    

    如果: 出现在文档中的其他位置,并且所需的值始终采用以下形式

    [2 character prefix]:[numerical]
    

    您可以通过将Where-Object 过滤器更改为:

    {$_ -match '.{2}:\d+'}
    

    【讨论】:

    • 我认为该文件有一个标题行(“ActiveFaults”)。输出文件也有一个标题行。在这种情况下,我认为使用import-csvexport-csv 代替get-contentout-file 是有意义的。
    • @dan-gph 是的,但如果文件很大,您会因使用单个属性 UniqueActiveFaults 创建对象而产生巨大开销,以便能够使用 @987654338 将其写回磁盘@。如果文件有多个列,这可能是有意义的,但在这种情况下,我认为这种权衡是不值得的
    • 大量开销?这听起来像是过早的优化。据我们所知,这些文件只有 10 行长。就目前而言,您的代码不符合要求,因为它不处理标头。顺便说一句,我认为 -ReadCount 不会帮助你。无论如何,排序对象都必须将整个文件加载到内存中。
    • @dan-gph 从示例来看,我不需要处理标题,我的代码仍然解决了 OP 的问题。关于RawReadCount:如果输入文件> 1GB ASCII,则它太大而无法放入单个字符串中,因此ReadCount 实际上会有所帮助。 Sort-Object 不需要加载整个文件,而是加载整个单独的字符串集 - 有很大的不同
    • 我可以在输入和输出示例文件中看到一个标题行。对于 ReadCount,我的意思是,如果您在 Get-Content 上使用 -ReadCount 1(默认值)或 -ReadCount 100,它不会对内存使用产生任何影响。我想它可能会加快一点。这听起来像是另一个过早的优化。
    【解决方案2】:

    由于马蒂亚斯不喜欢我的建议,我将在这里说明我的意思:

    Import-Csv .\Sample.csv | 
        % { $_.ActiveFaults -split '\s+' } | 
        Sort-Object -Unique | 
        Select-Object @{name='UniqueActiveFaults'; expr={ $_ } } | 
        Export-Csv IdealOutput.csv -NoTypeInformation
    

    输出如下:

    "UniqueActiveFaults"
    "00:1343"
    "00:1345"
    "00:1523"
    "90:1344"
    "90:5900"
    "90:8988"
    "BA:1330"
    "BA:8797"
    

    如果输入真的很大并且上面的代码无法有效地处理它,我会尝试将值通过管道传递到 .NET HashSet 中来代替 Sort-Object。

    【讨论】:

    • 噢!没有想过使用Select-Object 的计算表达式。从这个角度来看,你的建议确实很有意义,我当然喜欢它;-)
    • 感谢@Mathias,感谢您的基本想法。我希望我的 cmets 不会太烦人 :)
    【解决方案3】:
    @ECHO Off
    SETLOCAL
    :: remove variables starting $
    FOR  /F "delims==" %%a In ('set $ 2^>Nul') DO SET "%%a="
    (
     ECHO(UniqueAciveFaults
     FOR /f "delims=" %%a IN (q29884835.txt) DO FOR %%b IN (%%a) DO SET "$%%b=y"
     FOR /f "delims=$=" %%a IN ('set $^|find ":"') DO ECHO(%%a
    
    )>u:\newfile.csv
    
    GOTO :EOF
    

    我使用了一个名为 q29884835.txt 的文件,其中包含我的测试数据。

    产生 u:\newfile.csv

    嗯 - 它显然不是 powershell,但它可以工作。

    第一个for 清除从$ 开始的所有环境变量。通常没有,所以可能不需要。

    第二个for 行读取文件,然后为每个元素设置一个变量$elementcontentsy(它设置为某事很重要,某事 不是)

    第三个for 行选择集合$ 变量中包含:echoes 它们的那部分。

    【讨论】:

    • 这令人印象深刻。我向你脱帽致敬。但坦率地说,这段代码看起来非常可怕。为什么不学习 PowerShell? ;)
    猜你喜欢
    • 2016-03-29
    • 2015-09-28
    • 2023-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-05
    • 2018-03-24
    相关资源
    最近更新 更多