3

I am trying to return the sections of an INI file as an array of captures using a regular expression:

\[[^\]\r\n]+](?:\r?\n(?:[^\[\r\n].*)?)*

This works in just about every regex checker I have tried, including one using .NET, but when I try it in my program, it only selects the section headers, not the bodies.

I'm working in Powershell, but I'm explicitly creating a Regex object using New-Object, not the built-in -match operator. I'm at a bit of a loss since it works outside my environment but not inside it.

Update: Ansgar reminds me that I should have shown my code, so here it is.

$IniRegex = New-Object System.Text.RegularExpressions.Regex("\[[^\]\r\n]+](?:\r?\n(?:[^\[\r\n].*)?)*")
$TestIni = Get-Content "C:\Test.ini"
$SectionsMatches = $IniRegex.Matches($TestIni)

$SectionsMatches.Count
$SectionsMatches[0].Captures[0].ToString()
$SectionsMatches[1].Captures[0].ToString()

The Test.ini file contains some sample settings:

[Test0]
Setting0=0
Setting1=1

[Test1]
Setting2=2
Setting3=3

The output from the code is:

2
[Test0]
[Test1]
Graham Powell
  • 283
  • 1
  • 12
  • 1
    Please show your code, not just the regular expression. – Ansgar Wiechers Mar 21 '14 at 18:42
  • Code added. My terminology was also a bit loose, I'm not actually getting an array of groups, but an array of captures. I have updated the question to reflect this. – Graham Powell Mar 21 '14 at 19:38
  • Try adding `RegexOptions.Multiline` (http://msdn.microsoft.com/en-us/library/system.text.regularexpressions.regexoptions(v=vs.110).aspx)) – Lasse V. Karlsen Mar 21 '14 at 19:42
  • Do you *have* to use regular expressions? I'm asking because what if you have a section containing an entry with `name=[123]` in it? Wouldn't that throw a spanner in your system? Have you considered using the Win32 API that deals with ini files instead of implementing your own? – Lasse V. Karlsen Mar 21 '14 at 20:30
  • Lasse, got the same results as before. – Graham Powell Mar 21 '14 at 20:30
  • Well, regular expressions seemed an elegant way to handle it. If you have another solution I would be very interested. – Graham Powell Mar 21 '14 at 20:36
  • While the scope of your question has been answered here, you might want to take a look at a solution to the broader question--how to effectively access data in an INI file at [INI file parsing in PowerShell](http://stackoverflow.com/questions/417798/ini-file-parsing-in-powershell). Also, I have adapted Artem's answer from there into my open source PowerShell library, calling it [Get-IniFile](http://cleancode.sourceforge.net/api/powershell/CleanCode/FileTools/Get-IniFile.html). – Michael Sorens Mar 22 '14 at 20:03

1 Answers1

2

If you're on PowerShell 3 or higher, add -Raw to the end of Get-Content. By default, Get-Content returns an array of strings, with one element corresponding to one line. But you want to match against a single string:

$IniRegex = New-Object System.Text.RegularExpressions.Regex("\[[^\]\r\n]+](?:\r?\n(?:[^\[\r\n].*)?)*")
$TestIni = Get-Content "C:\Test.ini" -Raw
$SectionsMatches = $IniRegex.Matches($TestIni)

$SectionsMatches.Count
$SectionsMatches[0].Captures[0].ToString()
$SectionsMatches[1].Captures[0].ToString()

If you're on v2, you can do this instead:

$TestIni = (Get-Content "C:\Test.ini") -join ''

Also, you can shorten the line where you create the regex quite a bit, by using the [regex] type accelerator:

$IniRegex = [regex]"\[[^\]\r\n]+](?:\r?\n(?:[^\[\r\n].*)?)*"
KevinD
  • 3,023
  • 2
  • 22
  • 26
  • ...this seems so obvious in retrospect. Anyway: winner, winner, chicken dinner, thanks very much and goodnight. – Graham Powell Mar 21 '14 at 20:41
  • 1
    It's only obvious after you've already made the mistake a few times. :) – KevinD Mar 21 '14 at 20:47
  • I discovered that `$TestIni`, when passed to a method that requires a single string, replaces the original newlines with spaces. Searching for a space in the regex actually works. – Kendall Frey Mar 21 '14 at 20:55