4

I need help breaking a Bible search string with PHP into variables or tokens. I'd like to get an explicit usage example of the solution offered in this post: PHP preg_match bible scripture format.

EDIT: the chapter and verses (from, to) are optional.

For example: I'd like to be able to split any of the following strings:

'John 14:16–17'; //Book Chapter:FromVerse-ToVerse
'John 14:16'; //Book Chapter:FromVerse
'John 14'; //Book Chapter
'John'; //BOOK

The following:

<?php
$string = 'Exodus 1:3-7'; // Where Exodus is the book, 1 the chapter number, 3 the starting verse and 7 the ending verse. [Book Chapter:StartVerse-EndVerse]
$pattern = '/[ :-]/';
list( $book, $chapter, $from, $to ) = preg_split($pattern, $string );
echo $book;

Allows me to get the nbook name: Exodus. I could also retrieve the chapter number the same way (echo $chapter), etc.

The problem I'm having with this solution is when the book name has more than one word. Example '1 Samuel 3:4-5'. If I echo $book for example, I get offset 3 not defined or a similar error.

It was suggested in the post linked above that this regex pattern is more complete:

/\w+\s?(\d{1,2})?(:\d{1,2})?([-–]\d{1,2})?(,\s\d{1,2}[-–]\d{1,2})?+$/

I guess my question is how to use this pattern or a similar one to split the search string as described above.

A similar issue is discussed here: PHP problems parsing a bible book string, but I'm just having some trouble modifying the pattern. I keep getting errors like : Undefined offset: 3 ...

I'd appreciate your help

Community
  • 1
  • 1
TPM
  • 69
  • 8
  • You aren't using the matches array anywhere. Supply the array as the third argument to `preg_match()` like so: `if (preg_match($pattern, $string, $matches)) {` and then `print_r($matches);` to see what it contains. If that's not what you want, then update your question to state *how* you want it instead. – Amal Murali May 18 '14 at 08:02
  • @AmalMurali Thank you for the quick reply. print_r($matches) is returning 'Array ( [0] => 26 )'. I guess what I'd like instead is to split the string. Something like this maybe?: `list( $book, $chapter, $from, $to ) = preg_split( $pattern, 'Luke 1:26-38' )`. But It didn't work when I tried. The reason I'm using preg_match in the if statement is to control the outcome. This way, if the pattern isn't matched, we do something else, such as printing an error message. I apologize if I'm not describing the problem properly. Hopefully this helps. – TPM May 18 '14 at 08:19
  • That's better, but I'm still not sure what you would like the `$matches` array to contain when the input string is `1 John 14:16–17, 25–26`. Can you update your question to include some sample strings and their expected output? – Amal Murali May 18 '14 at 08:24
  • @AmalMurali, I've updated and clarified the question. Any chance you could have a look? – TPM May 18 '14 at 09:54

3 Answers3

4

I wouldn't do that with one regex.

After also reading the Bible-citation - Common formats section in Wikipedia, see my bible-parser idea:

$holy_str = 'Jonny 5:1,4-5,17,21;';

// split verses from book chapters
$parts = preg_split('/\s*:\s*/', trim($holy_str, " ;"));

// init book
$book = array('name' => "", 'chapter' => "", 'verses' => array());

// $part[0] = book + chapter, if isset $part[1] is verses
if(isset($parts[0]))
{
  // 1.) get chapter
  if(preg_match('/\d+\s*$/', $parts[0], $out)) {
    $book['chapter'] = rtrim($out[0]);
  }

  // 2.) book name
  $book['name'] = trim(preg_replace('/\d+\s*$/', "", $parts[0]));
}

// 3.) verses
if(isset($parts[1])) {
  $book['verses'] = preg_split('~\s*,\s*~', $parts[1]);
}

print_r($book);

output (test at eval.in):

Array
(
    [name] => Jonny
    [chapter] => 5
    [verses] => Array
        (
            [0] => 1
            [1] => 4-5
            [2] => 17
            [3] => 21
        )

)

No matter here, if John 14:16 or 12 John: 3, 16-17

Also see regex faq.

Community
  • 1
  • 1
Jonny 5
  • 11,051
  • 2
  • 20
  • 42
  • Thank you so much! Your solution meets and exceeds my expectations. Thanks to everyone else for their input. – TPM May 18 '14 at 10:06
  • @TPM Welcome :) happy, could have been of help. – Jonny 5 May 18 '14 at 10:07
  • one last question if I may, What about those times when a book name is alphanumeric, meaning it includes a leading digit like here: `1 John 14:16`, where '1 john' is the book name. How would you grab the book name in this case? In other words, If there no leading digits, just get the single word, but if the name is alpha numeric, grab the digit and the word. When the book name is alphanumeric, there's always a space between the leading digit and the word. Thanks already – TPM May 18 '14 at 13:04
  • 1
    @TPM Ooooh, there I had a misunderstanding, see updated answer, works? – Jonny 5 May 18 '14 at 13:12
  • 1
    you did it! Thank you sooo much! – TPM May 18 '14 at 13:18
  • very flexible and useful indeed. Much appreciated. – TPM May 18 '14 at 15:04
  • Amazing! I've been looking for something just like this for a long time. – Adam Jul 10 '18 at 17:20
2

Here you got regex solution I split regex into multiple lines so its easier for you to understand it. (I use named patterns)

$pattern = 
    "/".
        "(?P<book>\w+)\s?".
        "(".
            "(?P<chapter>\d{1,2})".
            "(".
                ":(?P<fromVerse>\d{1,2})".
                "(".
                    "(–|-)(?P<toVerse>\d{1,2})".
                ")?".
            ")?".
        ")?".
    "/";

Or more generic solution

$pattern = 
"/".
    "(?P<book>\w+)\s?".
    "(".
        "(?P<chapter>\d+((–|-)\d+)?)".
        "(".
            ":(?P<verse>(,?\d+((–|-)\d+)?)+)".
        ")?".
    ")?".
"/";

$string = 'Jonny 5:1,4-5,17,21';



$match = null;
preg_match($pattern,$string,$match);

$book = isset($match['book'])?$match['book']:null;
$chapter = isset($match['chapter'])?$match['chapter']:null;
$verses = isset($match['verse'])?explode(',', $match['verse']):null;

var_dump($book, $chapter, $verses);
mleko
  • 9,108
  • 5
  • 41
  • 68
  • 1
    @mleko thanks for your input. It looks good and I'm sure it will benefit many. – TPM May 18 '14 at 12:25
1

I have been working with the excellent library: https://github.com/openbibleinfo/Bible-Passage-Reference-Parser, which could do what you ask by converting to OSIS format and then being able to write a simple RegEx (preg_match) statement.

At the bottom of the referenced URL, the author references "This is the fourth complete Bible reference parser that I've written. It's how I try out new programming languages: the first one was in PHP (2002), which saw production usage on a Bible search website from 2002-2011..." In other words, if using JavaScript/Node.JS isn't practical, you may be able to contact the author to see if the PHP Bible reference parser is open source / available. I haven't run across it yet, but it may be out there on sourceforge or something like that.

Hope that helps,

Ryan

ryanm
  • 2,143
  • 16
  • 29