1

I have 2 issues with my below regex:

1) The output shows 2 extra (blank) element at the beginning and end (can't use PREG_SPLIT_NO_EMPTY as isbn can be empty).

2) Is it possible to get associative array from this? I mean I want output in the form of $output = array("title" => "Book1", "writer" => "writer1", "isbn" => "1234") in this format.

$val = "[title]Book1[/title][writer]Writer1[/writer][isbn]1234[/isbn]";
$pattern = '/\[title](.*?)\[\/title].*?\[writer](.*?)\[\/writer].*?\[isbn](.*?)\[\/isbn]/i';
$allparams = preg_split($pattern, $val, -1, PREG_SPLIT_DELIM_CAPTURE);

Output:

Array ( [0] => [1] => Book1 [2] => Writer1 [3] => 1234 [4] => )
4
  • 2
    So why are you using preg_split instead of preg_match for a matching job? Commented Nov 13, 2015 at 20:42
  • I need those 3 elements separately in an array. Can preg_match achive that? Commented Nov 13, 2015 at 20:44
  • 2
    preg_split isn't the right function to do that, you need to use preg_match with named captures and to filter numeric keys from the result array. Commented Nov 13, 2015 at 20:51
  • Also... the pattern can begin and end with any character, such as ~ or # or =. Use something other than / so you don't have to escape / every time you use it in the pattern. Commented Nov 13, 2015 at 20:54

2 Answers 2

2

preg_split isn't the way to go, use preg_match (or preg_match_all if your want several results from a single string):

$pattern = '~
    \[title]  (?<title>  [^[]+ ) \[/title]  .*?
    \[writer] (?<writer> [^[]+ ) \[/writer] .*?
    \[isbn]   (?<isbn>   [^[]+ ) \[/isbn]
~sx';

if (preg_match($pattern, $yourtext, $m)) {
    echo 'title:  ' . $m['title']  . PHP_EOL
       . 'writer: ' . $m['writer'] . PHP_EOL
       . 'isbn:   ' . $m['isbn']   . PHP_EOL;
}

The s flag allows the dot to match newlines, and the x flag ignores whitespaces in the pattern.

Note that you can filter numeric keys since PHP 5.6 like this (not tested):

$result = array_filter($m, function ($k) { return !is_numeric($k); }, ARRAY_FILTER_USE_KEY);

If you don't have PHP >= 5.6, a simple foreach loop can do the job:

$result = array();
foreach ($m as $k=>$v) {
    if (is_numeric($k) || empty($v)) continue;
    $result[$k] = $v;
}
Sign up to request clarification or add additional context in comments.

2 Comments

wow ..I wasn't aware of such regex syntax. Thanks a lot. One last thing, is it possible to make it work even if some elements are missing (for example no [writer][/writer] block)? So I mean all three elements should be optional, "if found" basis.
@AbdFahim: You can always enclose each bbcode tag in an optional group, like this (?:\[title]...\[/title] .*?)? etc., but don't forget to take care that at least one group exists (perhaps you can start the pattern with a lookahead assertion, something like (?=\[) or (?=\[(?:title|writer|isbn)])) or more simple you check if $m doesn't only contain empty items.
1

something like that will do the trick, even if it's not the best way I think...

$a = "/\[(.*)\](.*)\[\/.*\]/U";
$val = "[title]Book1[/title][writer]Writer1[/writer][isbn]1234[/isbn]";
$array = json_decode('{'.substr(preg_replace($a, ',"$1":"$2"', $val), 1).'}');

BTW, this one find every [TAG] and put it in the array (whenever how many tag you use). Be careful, as it use JSON, it may have some problem with " or unexpected string not in [tag]string[/tag] pattern.

1 Comment

This is intelligent, and serves the purpose. Thanks, I learned something today. This time I'll go with Casimir's solution though.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.