powershellcsvimport-csv

How to prevent Import-Csv ignores empty lines?


Background

I have a CSV file that contains both empty line and line breaks in cells like this:


Richard Roe
Mary Major
"Alex Smith
Betty Brown
Chris Clark"

"Betty Brown
Donna Davis
Edward Evans"

Alex Smith

"Richard Roe
Mary Major"

Input CSV

I am trying to write a PowerShell script to read this CSV file and get the following result (insert the name of their department in the list):


"Legal
Richard Roe"
"Accounting
Mary Major"
"R&D
Alex Smith
Betty Brown
Design
Chris Clark"

"R&D
Betty Brown
Design
Donna Davis
Edward Evans"

"R&D
Alex Smith"

"Legal
Richard Roe
Accounting
Mary Major"

Expected Result

Problem

When you read the CSV file with Import-CSV, it ignores the empty lines like this:

PS C:\tmp> Import-Csv -Header Names .\NamesList.csv

Names
-----
Richard Roe
Mary Major
Alex Smith...
Betty Brown...
Alex Smith
Richard Roe...


PS C:\tmp>

What I expected is:

Names
-----

Richard Roe
Mary Major
Alex Smith...

Betty Brown...

Alex Smith

Richard Roe...

You can switch to Get-Content to read the CSV, but it seems difficult to process the line breaks in the cell.

Question

Is there any way to prevent Import-Csv ignores empty lines?

Edit

Department-Name mappings in the another CSV file like this:

Legal,Richard Roe
Accounting,Mary Major
R&D,Alex Smith
R&D,Betty Brown
Design,Chris Clark
Design,Donna Davis
Design,Edward Evans

Solution

  • Import-Csv doesn't have this option but you can work your way around it by reading line by line and parsing the content. It requires a bit of work though.

    Assuming the file look exactly like you have shared in your question, something like this approach might work:

    function Read {
        param(
            [Parameter(Mandatory)] [string] $Path,
            [Parameter(Mandatory)] [string] $Header
        )
    
        $Path = Convert-Path $Path -ErrorAction Stop
        $sb = [System.Text.StringBuilder]::new()
        $startQuote = $false
    
        foreach ($line in [System.IO.File]::ReadLines($Path)) {
            if ($line.StartsWith('"')) {
                $startQuote = $true
                $sb = $sb.AppendLine($line.TrimStart('"'))
                continue
            }
    
            if (-not $startQuote) {
                [pscustomobject]@{ $Header = $line }
                continue
            }
    
            if ($line.EndsWith('"')) {
                $startQuote = $false
                $sb = $sb.Append($line.TrimEnd('"'))
                [pscustomobject]@{ $Header = $sb.ToString() }
                $sb = $sb.Clear()
                continue
            }
    
            $sb = $sb.AppendLine($line.TrimStart('"'))
        }
    }
    
    $names = Read .\NamesList.csv -Header Names
    $names
    

    This will output an array of objects like the one you're expecting:

    Names
    -----
    
    Richard Roe
    Mary Major
    Alex Smith…
    
    Betty Brown…
    
    Alex Smith
    
    Richard Roe…
    

    Then you can prepend the Departments by updating the objects by index. It's unclear where these are coming from, if we had this information the these could be prepended earlier for a more efficient approach:

    $names[1].Names = "Legal`n$($names[1].Names)"
    $names[2].Names = "Accounting`n$($names[2].Names)"
    $names[3].Names = "R&D`n$($names[3].Names)"
    $names | ConvertTo-Csv # <= This for demo, it should be `Export-Csv` instead
    

    Then the resulting CSV would look like what you are expecting:

    "Names"
    ""
    "Legal
    Richard Roe"
    "Accounting
    Mary Major"
    "R&D
    Alex Smith
    Betty Brown
    Chris Clark"
    ""
    "Betty Brown
    Donna Davis
    Edward Evans"
    ""
    "Alex Smith"
    ""
    "Richard Roe
    Mary Major"