CSV文件中的多个全局查找替换

时间:2015-08-25 12:15:53

标签: powershell csv replace

我一直在努力解决我认为非常简单的问题,但我无法看到它。我有一堆30多个csv文件,每天由不同的应用程序生成不同的内容,我需要在导入到单个报告数据库之前进行规范化。提取,转换和加载(ETL)类型的东西 - 全局查找和替换。

循环浏览文件没问题 - 不确定使用ForEach-Object Fullname是否是输出到' OUT'的最佳方式。文件夹搞砸了但是使用-Name意味着我必须包含路径。

基本上,所有' True' False'文本将被替换为1/0,与' yes' /' no',poweredon / poweredoff等相同。此外,我们有4个站点 - 每个站点都需要替换为ref。 id,这样的东西。我已经尝试修改我在网上找到的大量脚本 - 很多都在这里。尝试使用数组中的替换文本,将CSV拉成字符串,只是无法看到它。我用VBScript多年来一直在做同样的事情,这很简单。但是我需要学习PowerShell,所以我要坚持下去。

3 个答案:

答案 0 :(得分:1)

  

基本上,所有'True'/'False'文本都将替换为1/0,同样是'yes'/'no',poweredon / poweroff等。此外,我们有4个站点 - 每个站点需要替换为REF。 id,这样的东西。我已经尝试修改我在网上发现的大量脚本 - 很多都在这里。尝试使用数组中的替换文本,将csv拉入字符串,只是看不到它。多年来我一直用vbscript做同样的事情,这很简单。但我需要学习PShell,所以我要坚持下去。我真的很感激这里的一些帮助。

如果它是静态的,你可能会逃脱:

$changes = @{
  'true' = '1';
  'false' = '0';
  'poweredon' = '1';
  'poweredoff' = '0'
}
$folder = "" # your folder here
$csvFiles = ls $folder *.csv
foreach ($file in $csvFiles) {
  $csvData = import-csv $file
  foreach ($row in $csvData) {
    $cells = $row | `
       gm | `
       ?{$_.MemberType -eq 'NoteProperty'} | `
       select -exp Name
    foreach ( $cell in $cells ) {
       $val = $row."$cell"
       $valueNeedsChanging = $changes.ContainsKey($val)
       if ( $valueNeedsChanging ) {
          $newValue = $changes[$val]
          $row."$cell" = $newValue
       }
    }
  }
  cp $file.FullName "$($file.FullName).bak" # back it up before saving
  $csvData | export-csv -Path $file.FullName -NoTypeInformation
}

我选择使用Import-Export-CSV为具有大量高级格式的文件保留CSV文件的结构。

答案 1 :(得分:0)

好的,这是一个快速搜索和替换功能。它可以读取多个CSV文件并匹配\替换多个值。

function Replace-CsvValue
{
    [CmdletBinding()] # Enable pipeline support
    Param
    (
        # Filename, mandatory, takes pipeline input
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        # Alias, allows to directly pipe Get-ChildItem output to this function
        [Alias('FullName')]
        [string]$File,

        # Scriptblock, mandatory, does actual search and replace
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [scriptblock]$ScriptBlock
    )

    Process
    {
        # Import CSV
        $CsvFile =  $File | Import-Csv

        # Generate new filename
        $NewFileName = Join-Path -Path (Split-Path -Path $File -Parent) -ChildPath ('Processed_' + (Split-Path -Path $File -Leaf))

        # Iterate over each line in CSV
        $CsvFile | ForEach-Object {
            # Execute scritblock against record
            & $ScriptBlock
        }

        # Export CSV
        $CsvFile | Export-Csv -Path $NewFileName -NoTypeInformation
    }
}

用法:

  • 使用必需的替换逻辑编写scriptblock
  • 管道文件名或Get-ChildItem输出到函数并传递scriptblock

实施例

原始CSV文件:

State, Active, Available
PoweredOn, True, Yes

函数调用:

# Scriptblock with replace logic
$ReplaceRule = {

    # Iterate over each item in CSV line
    $Item = $_
    $_.PSObject.Properties.Name | ForEach-Object {

        # If item name matches...
        switch ($_)
        {
            'State' {
                # If item value matches...
                if($Item.$_ -eq 'PoweredOn')
                {
                    $Item.$_ = 'Online'
                }
                # Or if item value matches...
                elseif($Item.$_ -eq 'PoweredOff')
                {
                    $Item.$_ = 'Offline'
                }
                break
            }

            # More replace rules, you can add your own here...

            'Active' {
                if($Item.$_ -eq 'True')
                {
                    $Item.$_ = '1'
                }
                elseif($Item.$_ -eq 'False')
                {
                    $Item.$_ = '0'
                }
                break
            }

            'Available' {
                if($Item.$_ -eq 'Yes')
                {
                    $Item.$_ = '1'
                }
                elseif($Item.$_ -eq 'No')
                {
                    $Item.$_ = '0'
                }
                break
            }
        }
    }
}

# Get all CSV files that match wildcard and
# feed them to the Replace-CsvValue function

Get-ChildItem -Path '.\' -Filter '*Report*.csv' | Replace-CsvValue -ScriptBlock $ReplaceRule

已处理的CSV文件:

"State","Active","Available"
"Online","1","1"

答案 2 :(得分:0)

我在in android.R.drawable的帮助下制作了这个用于测试的csv。请注意,某人的名字是True。我在那里检查以确保我的逻辑正常。

Present Name             Lunch State      
------- ----             ----- -----      
TRUE    Jesse Daniels    No    Powered Off
FALSE   Debra Cunningham Yes   Powered Off
TRUE    True Jones       Yes   Powered Off
TRUE    George Fernandez Yes   Powered Off
FALSE   Lisa Cox         No    Powered On 

出于这个目的,我认为忽略它是一个CSV并且只是直接替换文本这一事实很简单。我们必须小心的警告是部分匹配。使用正则表达式我们应该能够解释这种可能性。

您已经知道可以链接-replace的评论。让我们在那里添加一些正则表达式魔法,以使过程更容易。

$filename = "C:\temp\MOCK_DATA.csv"
$oneKeywordPattern = "Powered On","Yes","True" -join "|"
$zeroKeywordPattern = "Powered Off","No","False" -join "|"
(Get-Content $filename) -replace "(?<=^|,)$oneKeywordPattern(?=$|,)","1" -replace "(?<=^|,)$zeroKeywordPattern(?=$|,)","0" | Set-Content $filename

为了确保考虑csv结构,我们只在元素位于行的开头或逗号后面的行或逗号(这是使用Mockaroo)时替换。这也确保我们只更改完整元素,并且True J​​ones不受影响。

我们使用$oneKeywordPattern,以便您可以向需要更改为1的数组添加元素。我们使用管道将它们连接起来,以便将其视为替代正则表达式模式。其对应$zeroKeywordPattern的功能相同。

<强>输出

Present Name             Lunch State
------- ----             ----- -----
1       Jesse Daniels    0     0    
0       Debra Cunningham 1     0    
1       True Jones       1     0    
1       George Fernandez 1     0    
0       Lisa Cox         0     1    

您可能有其他模式不需要使用此逻辑进行更改。只需链接另一个-replace并记住它支持正则表达式,所以要注意特殊字符。

这里的两个警告是,如果文件很大,可能需要一段时间来加载文件并处理正则表达式(特别是如果你添加更多。)此外,如果你的文字用引号括起来,我们目前不会考虑但这很容易。