سؤال

I'm looking for help writing a regular expression with PHP. Coming in I have the data as follows:

3 1/2 cups peeled and diced potatoes

1/3 cup diced celery

1/3 cup finely chopped onion

2 tablespoons chicken bouillon granules

I have this all in a single variable. I now am parsing it out so that it stores as 3 different usable data items.

I've not ever written a regular expression before, and I found this guide here - http://www.noupe.com/php/php-regular-expressions.html but I'm still struggling to take that and apply it to my situation. I also do not know how many rows will be coming in, it could be 1 or it could be 100.

This is what I have so far. I have tested the code around the preg_match statement and it's working.

preg_match_all("",
    $post_meta,
    $out, PREG_PATTERN_ORDER);

What should I put between the "" in the preg_match_all statement to achieve the desired parsing? Thanks upfront for any help you can give!

EDIT

the desired output for the example input would be:

$var1 = 3 1/2
$var2 = cups
$var3 = peeled and diced potatoes

so then I can run functions to store the data:

update_database($var1);
update_database($var2);
update_database($var3);

repeat for each row. It doesn't have to be 3 different variables, an array would be fine too.

هل كانت مفيدة؟

المحلول

You can break it apart with an expression like this:

$string = '3 1/2 cups peeled and diced potatoes

1/3 cup diced celery

1/3 cup finely chopped onion

2 tablespoons chicken bouillon granules';

preg_match_all('~([0-9 /]+)\s+(cup|tablespoon)s?\s+([-A-Z ]+)~i', $string, $matches);

That will give you this if you print $matches:

Array
(
    [0] => Array
        (
            [0] => 3 1/2 cups peeled and diced potatoes
            [1] => 1/3 cup diced celery
            [2] => 1/3 cup finely chopped onion
            [3] => 2 tablespoons chicken bouillon granules
        )

    [1] => Array
        (
            [0] => 3 1/2
            [1] => 1/3
            [2] => 1/3
            [3] => 2
        )

    [2] => Array
        (
            [0] => cup
            [1] => cup
            [2] => cup
            [3] => tablespoon
        )

    [3] => Array
        (
            [0] => peeled and diced potatoes
            [1] => diced celery
            [2] => finely chopped onion
            [3] => chicken bouillon granules
        )

)

Although this part isn't really necessary, you can restructure the array to put each item in the format you are asking for. (You can write to the db without putting them in this order, but I will demonstrate here how to put them into the order you are looking for.)

$info_array = array();

for ($i = 0; $i < count($matches); $i++) {

    for ($j = 1; $j < count($matches[$i]); $j++) {
        $info_array[$i][] = $matches[$j][$i];
    }

}

If you printed $info_array, you'd see this:

Array
(
    [0] => Array
        (
            [0] => 3 1/2
            [1] => cup
            [2] => peeled and diced potatoes
        )

    [1] => Array
        (
            [0] => 1/3
            [1] => cup
            [2] => diced celery
        )

    [2] => Array
        (
            [0] => 1/3
            [1] => cup
            [2] => finely chopped onion
        )

    [3] => Array
        (
            [0] => 2
            [1] => tablespoon
            [2] => chicken bouillon granules
        )

)

You can now loop through that array to put the items into the database:

for ($i = 0; $i < count($info_array); $i++) {

    foreach ($info_array[$i] AS $ingredient) {
        // INSERT INTO DATABASE HERE
        print "<BR>update_database(".$ingredient.")";
    }

}

So that would do what you are asking, but I'm assuming that you have some columns that you want to assign these to. You can do something like this if you want to put each piece into its own column:

$info_array = array();

for ($i = 0; $i < count($matches); $i++) {

    for ($j = 1; $j < count($matches[$i]); $j++) {

        if ($j == 1) {$key = 'amount';}
        elseif ($j == 2) {$key = 'size';}
        elseif ($j == 3) {$key = 'ingredient';}

        $info_array[$i][$key] = $matches[$j][$i];
    }

}
print "<PRE><FONT COLOR=ORANGE>"; print_r($info_array); print "</FONT></PRE>";

for ($i = 0; $i < count($info_array); $i++) {

    foreach ($info_array[$i] AS $ingredient) {
        print "<BR>update_database(".$ingredient.")";
    }

}

foreach ($info_array AS $ingredient_set) {

    $sql = "INSERT INTO table SET Amount = '".$ingredient_set['amount']."', Size = '".$ingredient_set['size']."', Ingredient = '".$ingredient_set['ingredient']."'";
    print "<BR>".$sql;

}

That would give you something like this:

INSERT INTO table SET Amount = '3 1/2', Size = 'cup', Ingredient = 'peeled and diced potatoes'
INSERT INTO table SET Amount = '1/3', Size = 'cup', Ingredient = 'diced celery'
INSERT INTO table SET Amount = '1/3', Size = 'cup', Ingredient = 'finely chopped onion'
INSERT INTO table SET Amount = '2', Size = 'tablespoon', Ingredient = 'chicken bouillon granules'

EDIT: Explanation of the REGEX

([0-9 /]+)    \s+    (cup|tablespoon)s?    \s+    ([-A-Z ]+)
     ^         ^              ^             ^          ^
     1         2              3             4          5
  1. ([0-9 /]+) Looking for a digit here to capture the amount of whatever measurement you will need. The [0-9] is a character class that means only grab numbers falling between 0 and 9. Also inside the character class, I added a space and a forward slash to accomodate measurements like 3 1/2. The + sign means that it has to have at least one of those to make the match. Finally, the parenthesis around this part tell PHP to capture the value and store it as part of the $matches array so we can do something with it later.
  2. \s+ Look for a whitespace character. Because of the +, we need it to contain at least one, but could be more than one space. I changed this from my initial code just in case there was more than one space.
  3. (cup|tablespoon)s? This is basically an "OR" statement. It's looking for either cup or tablespoon. It can also have an s after it as in cups or tablespoons, but the ? means that it doesn't have to be there. (The s can be there, but doesn't have to be.) In this "OR" statement, you would probably want to add other things like teaspoon|pint|quart|gallon|ounce|oz|box, etc. Each item separated by a | is just another thing that it could match. The parenthesis here will capture whatever it matched and store it so we can use it later.
  4. \s+ Same as number 2.
  5. ([-A-Z ]+) The character class [A-Z] looks for any letters. Actually any UPPERCASE letters, but you'll notice that after the expression, I use the case-insensitive i flag. This makes it so that it will match uppercase or lowercase letters. Also to this class, I have added a few other characters: the - and a space. If you run into any other characters like that that make the match fail, you can just add those characters into the class. (For instance, you may have an in apostrophe as in 1 Box Sara Lee's Cake Mix. Just add in the apostrophe into that class after the space.) The + sign means to find at least one of those characters in that class and the parenthesis capture whatever it found and saves it so that we can work with it later.

Hopefully that helps!

نصائح أخرى

How about:

preg_match_all("~^([\d/ ]+?)\s+(\w+)\s+(.+)$~",
    $post_meta,
    $out, PREG_PATTERN_ORDER);

You can try this:

preg_match_all('/([\d\s\/]+)\s+(\w+)\s+(.*)$/',
    $post_meta,
    $out, PREG_PATTERN_ORDER);


$var1 = $out[1][0];
$var2 = $out[2][0];
$var3 = $out[3][0];

This is wat you need to pass as pattern :

/([\d\s/]+)\s+(\w+)\s+(.*)$

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top