开发者

Parse file contents into array of section headings and section lines

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜