xBarns
xBarns

Reputation: 291

Reading list style text file into powershell array

I am provided a list of string blocks in a text file, and i need this to be in an array in powershell.

The list looks like this

a:1
b:2
c:3
d:
e:5
[blank line]
a:10
b:20
c:30
d:
e:50
[blank line]
... 

and i want this in a powershell array to further work with it.

Im using

$output = @()

Get-Content ".\Input.txt" | ForEach-Object {

    $splitline = ($_).Split(":")

    if($splitline.Count -eq 2) {

        if($splitline[0] -eq "a") {
            #Write-Output "New Block starting"
            $output += ($string)
            $string = "$($splitline[1])"

        } else {

            $string += ",$($splitline[1])"
        }
    }    
}

Write-Host $output -ForegroundColor Green

$output | Export-Csv ".\Output.csv" -NoTypeInformation
$output | Out-File ".\Output.txt"

But this whole thing feels quite cumbersome and the output is not a csv file, which at this point is i think because of the way i use the array. Out-File does produce a file that contains rows that are separated by commas.

Maybe someone can give me a push in the right direction.

Thx x

Upvotes: 2

Views: 983

Answers (2)

AdminOfThings
AdminOfThings

Reputation: 25001

One solution is to convert your data to an array of hash tables that can be read into a custom object. Then the output array object can be exported, formatted, or read as required.

$hashtables = (Get-Content Input.txt) -replace '(.*?):','$1=' | ConvertFrom-StringData
$ObjectShell = "" | Select-Object ($hashtable.keys | Select-Object -Unique)

$output = foreach ($hashtable in $hashtable) {
  $obj = $ObjectShell.psobject.Copy()
  foreach ($n in $hashtable.GetEnumerator()) {
    $obj.($n.key) = $n.value
  }
  $obj
}
$output
$output | Export-Csv Output.csv -NoTypeInformation

Explanation:

The first colons (:) on each line are replaced with =. That enables ConvertFrom-StringData to create an array of hash tables with values on the LHS of the = being the keys and values on the RHS of the = being the values. If you know there is only one : on each line, you can make the -replace operation simpler.

$ObjectShell is just an object with all of the properties your data presents. You need all of your properties present for each line of data whether or not you assign values to them. Otherwise, your CSV output or table view within the console will have issues.

The first foreach iterates through the $hashtables array. Then we need to enumerate through each hash table to find the keys and values, which is performed by the second foreach loop. Each key/value pair is stored as a copy of $ObjectShell. The .psobject.Copy() method is used to prevent references to the original object. Updating data that is a reference will update the data of the original object.

$output contains the array of objects of all processed data.

Usability of output:

# Console Output

$output | format-table

a  b  c  d e
-  -  -  - -
1
   2
      3

           5

10
   20
      30

           50

# Convert to CSV

$output | ConvertTo-Csv -NoTypeInformation
"a","b","c","d","e"
"1",,,,
,"2",,,
,,"3",,
,,,"",
,,,,"5"
,,,,
"10",,,,
,"20",,,
,,"30",,
,,,"",
,,,,"50"

# Accessing Properties

$output.b
2
20

$output[0],$output[1]

a : 1
b :
c :
d :
e :

a :
b : 2
c :
d :
e :

Alternative Conversion:

$output = ((Get-Content Input.txt -raw) -split "(?m)^\r?\n") | Foreach-Object { 
  $data = $_ -replace "(.*?):(.*?)(\r?\n)",'"$1":"$2",$3'
  $data = $data.Remove($data.LastIndexOf(','),1)
  ("{1}`r`n{0}`r`n{2}" -f $data,'{','}') | ConvertFrom-Json
}
$output | ConvertTo-Csv -NoType

Alternative Explanation:

Since ConvertFrom-StringData does not guarantee hash table key order, this alternative readies the file for a JSON conversion. This will maintain the property order listed in the file provided each group's order is the same. Otherwise, the property order of the first group will be respected.

All properties and their respective values are divided by the first : character on each line. The property and value are each surrounded by double quotes. Each property line is separated by a ,. Then finally the opening { and closing } are added. The resulting JSON-formatted string is converted to a custom object.

Upvotes: 2

Avshalom
Avshalom

Reputation: 8889

You can split by \n newline, see example:

$text = @"
a:1
b:2
c:3
d:
e:5

a:10
b:20
c:30
d:
e:50
e:50
e:50

e:50
"@

$Array = $text -split '\n' | ? {$_}
$Array.Count
15

if you want to exclude the empty lines, add ? {$_}

With your example:

$Array = (Get-Content ".\Input.txt") -split '\n' | ? {$_}

Upvotes: 1

Related Questions