I've got a string like this:
####################
Section One
####################
Data A
Data B
####################
Section Two
####################
Data C
Data D
etc.
I want to parse it into something like:
$arr(
'Section One' => array('Data A', 'Data B'),
'Section Two' => array('Data C', 'Data D')
)
At first I tried this:
$sections = preg_split("/(\r?\n)(\r?\n)#/", $file_content);
The problem is, the file isn't perfectly clean: sometimes there are different numbers of blank lines between the sections, or blank spaces between data rows.
The section head pattern itself seems to be relatively consistent:
####################
Section Title
####################
The number of #'s is probably consistent, but I don't want to count on it. The white space on the title 开发者_开发技巧line is pretty random.
Once I have it split into sections, I think it'll be pretty straightforward, but any help writing a killer reg ex to get it there would be appreciated. (Or if there's a better approach than reg ex...)
I'd take a multi-step approach:
- split into section headings/content
- parse each heading/content pair into the desired array structure
Here's an example, split into multiple lines so you can track what is going on:
Note the lack of sanity checking, this assumes nice, neat heading/content groups.
The regex was written for brevity and may or may not be sufficient for your needs.
// Split string on a line of text wrapped in lines of only #'s
$parts = preg_split('/^#+$\R(.+)\R^#+$/m', $subject, null, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
// Tidy up leading/trailing whitespace for each heading/content-block
$parts = array_map('trim', $parts);
// Chunk into array("heading", "content")
$parts = array_chunk($parts, 2);
// Create the final array
$sections = array();
foreach ($parts as $part) {
$sections[$part[0]] = explode("\n", $part[1]);
}
// Lets take a look
var_dump($sections);
I was able to quickly wrote this up:
<?php
$text = <<<EOT
####################
Section One
####################
Data B.Thing=bar#
.##.#%#
####################
Empty Section!
####################
####################
Last section
####################
Blah
Blah C# C# C#
EOT;
$entries = array_chunk(
preg_split("/^#+/m", $text, null, PREG_SPLIT_NO_EMPTY),
2
);
$sections = array();
foreach ($entries as $entry) {
$key = trim($entry[0]);
$value = preg_split("/\n/", $entry[1], null, PREG_SPLIT_NO_EMPTY);
$sections[$key] = $value;
}
print_r($sections);
?>
The output is: (as run on ideone.com)
Array
(
[Section One] => Array
(
[0] => Data B.Thing=bar#
[1] => .##.#%#
)
[Empty Section!] => Array
(
)
[Last section] => Array
(
[0] => Blah
[1] => Blah C# C# C#
)
)
any help writing a killer reg ex to get it there would be appreciated
...I have your killer regex pattern -- it relies on the \G
(continue) metacharacter to match the variably occurring lines of text after each section heading.
This technique is more optimal than previous answers because there is a single preg_
call and zero iterated function calls.
Sample Input:
$fileContents = <<<TEXT
####################
Section One
####################
Data A
Data B
####################
Section Two
####################
Data C
Data D
Data E
####################
Section Three
####################
Data F
TEXT;
Code: (Demo)
preg_match_all(
'~(?:
^\#{3,}\R
\h*(\S+(?:\h\S+)*)\h*\R
\#{3,}
|
\G(?!\A)
)
\R
(?!\#{3,})(.+)
~mx',
$fileContents,
$out,
PREG_SET_ORDER
);
foreach ($out as $set) {
$heading = $set[1] ?: $heading;
$result[$heading][] = $set[2];
}
var_export($result ?? 'No qualifying data');
Output:
array (
'Section One' =>
array (
0 => 'Data A',
1 => 'Data B',
),
'Section Two' =>
array (
0 => 'Data C',
1 => 'Data D',
2 => 'Data E',
),
'Section Three' =>
array (
0 => 'Data F',
),
)
Breakdown:
~ #starting pattern delimiter
(?: #start non-capturing group 1
^ #match the start of a line
\#{3,} #match 3 or more hash symbols
\R #match a newline sequence
\h* #match space or tab, zero or more times
( #start capture group 1
\S+ #match one or more non-whitespace characters
(?: #start non-capturing group 2
\h #match space or tab
\S+ #one or more non-whitespace characters
)* #end capture group 2, permit zero or more occurrences
) #end capture group 1
\h* #match space or tab, zero or more times
\R #match a newline sequence
\#{3,} #match 3 or more hash symbols
| #or
\G(?!\A) #continue matching but disallow starting from start of string
) #end non-capturing group 1
\R #match a newline sequence
(?! #start negative lookahead
\#{3,} #match 3 or more hash symbols
) #end negative lookahead
(.+) #match the whole line excluding the trailing newline characters
~ #ending pattern delimiter
m #pattern modifier: demand that ^ matches start of lines
x #pattern modifier: allow meaningless whitespaces in pattern for improved readability
...this was a fun necropost.
精彩评论