Split this values part of an insert query - php

Is there any way to achieve the following? I need to take this $query and split it into its various elements (the reason is because I am having to reprocess an insert query). As you can see this will work for regular string blocks or numbers, but not where a number, occurs in the string. Is there a way to say |\d but not where that \d occurs within a ' quoted string '?
$query = "('this is\'nt very, funny (I dont think)','is it',12345,'nope','like with 2,4,6')";
$matches = preg_split("#',|\d,#",substr($query,1,-1));
echo $query;
print'<pre>[';print_r($matches);print']</pre>';
So just to be clear about expected results:
0:'this is\'nt very, funny (I dont think)'
1:'it is'
2:12345
3:'nope'
4:'like with 2,4,6'.
** Additionally I don't mind if each string is not quoted - I can requote them myself.

Could (*SKIP)(*F) parts that are inside single quotes and match , outside:
'(?:\\'|[^'])*'(*SKIP)(*F)|,
(?:\\'|[^']) Inside the single quotes matches escaped \' or a character that is not a single quote.
See Test at regex101.com
$query = "('this is\'nt very, funny (I dont think)','is it',12345,'nope','like with 2,4,6')";
$matches = preg_split("~'(?:\\\\'|[^'])*'(*SKIP)(*F)|,~", substr($query,1,-1));
print_r($matches);
outputs to (test at eval.in)
Array
(
[0] => 'this is\'nt very, funny (I dont think)'
[1] => 'is it'
[2] => 12345
[3] => 'nope'
[4] => 'like with 2,4,6'
)
Not absolutely sure, if that is what you mean :)

('(?:(?!(?<!\\)').)*')|(\d+)
Try this.Grab the captures.Each string is quoted as well.See demo.
http://regex101.com/r/dK1xR4/3

You could try matching through preg_match_all instead of splitting.
<?php
$data = "('this is\'nt very, funny (I dont think)','is it',12345,'nope','like with 2,4,6')";
$regex = "~'(?:\\\\'|[^'])+'|(?<=,|\()[^',)]*(?=,|\))~";
preg_match_all($regex, $data, $matches);
print_r($matches[0]);
?>
Output:
Array
(
[0] => 'this is\'nt very, funny (I dont think)'
[1] => 'is it'
[2] => 12345
[3] => 'nope'
[4] => 'like with 2,4,6'
)

If you don't mind using preg_match, then the solution could look like this. This regex uses lookbehind with negative assertions (?<!\\\\), it will match strings inside quotes that is not preceded by slash, and the alternation with the vertical bar ensures that numbers that are part of larger match will be ignored.
$query = "('this is\'nt very, funny (I dont think)','is it',12345,'nope','like with 2,4,6',6789)";
preg_match_all( "/(?<!\\\\)\'.+?(?<!\\\\)\'|\d+/", substr( $query, 1, -1 ), $matches );
print_r( $matches );
/* output:
Array (
[0] => Array
(
[0] => 'this is\'nt very, funny (I dont think)'
[1] => 'is it'
[2] => 12345
[3] => 'nope'
[4] => 'like with 2,4,6'
[5] => 6789
)
)
*/

,(?=(?:[^']*'[^']*')*[^']*$)
Try this.This will split according to what you want.Replace by \n.See demo.
http://regex101.com/r/dK1xR4/4

Related

Regex to Match Passed Function/Method Parameters

I've had a good look around for a question that asked this before; alas, my search for a PHP preg_match search returned no results (maybe my searching skills fell short, I suppose justified considering it's a Regex question!).
Consider the text below:
The quick __("brown ") fox jumps __('over the') lazy __("dog")
Now currently I need to 'scan' for the given method __('') above, whereas it could include the spacing and different quotations ('|"). My best attempt after numerous 'iterations':
(__\("(.*?)"\))|(__\('(.*?)'\))
Or at its simplest form:
__\((.*?)\)
To break this down:
Anything that starts with __
Escaped ( and quotation mark " or '. Thus, \(\"
(.*?) Non-greedy match of all characters
Escaped closing " and last bracket.
| between the two expressions match either/or.
However, this only gets partial matches, and spaces are throwing off the search entirely. Apologies if this has been asked before, please link me if so!
Tester Link for the pattern provided above:
PHP Live Regex Test Tool
When the searched method string uses single quotes it will end up in another capture group than if it has double quotes. So in fact, your regular expression works (except for the spaces, see further down), but you'd have to look at a different index in your result array:
$input = 'The quick __("brown ") fox jumps __(\'over the\') lazy __("dog")';
// using your regular expression:
$res = preg_match_all("/(__\(\"(.*?)\"\))|(__\('(.*?)'\))/", $input, $matches);
print_r ($matches);
Note that you need preg_match_all instead of preg_match to get all matches.
Output:
Array
(
[0] => Array
(
[0] => __("brown ")
[1] => __('over the')
[2] => __("dog")
)
[1] => Array
(
[0] => __("brown ")
[1] =>
[2] => __("dog")
)
[2] => Array
(
[0] => brown
[1] =>
[2] => dog
)
[3] => Array
(
[0] =>
[1] => __('over the')
[2] =>
)
[4] => Array
(
[0] =>
[1] => over the
[2] =>
)
)
So, the result array has 5 elements, the first one representing the complete match, and all the others correspond to the 4 capture groups you have in your regular expression. As the capture groups for single quotes are not those of the double quotes, you'll find the matches at different places.
To "solve" this, you could use a back reference in your regular expression, which would look back to see which was the opening quote (single or double) and require the same to be repeated at the end:
$res = preg_match_all("/__\(([\"'])(.*?)\\1\)/", $input, $matches);
Note the back reference \1 (the backslash had to be escaped with another one). This refers back to the first capture group, where we have ["'] (again an escape was necessary) to match both kinds of quotes.
You also wanted to deal with spaces. On your PHP Live Regex you used a test string that had such spaces between the brackets and quotes. To deal with these so they still match the method strings correctly, the regular expression should get two additional \s*:
$res = preg_match_all("/__\(\s*([\"'])(.*?)\\1\s*\)/", $input, $matches);
Now the output is:
Array
(
[0] => Array
(
[0] => __("brown ")
[1] => __('over the')
[2] => __("dog")
)
[1] => Array
(
[0] => "
[1] => '
[2] => "
)
[2] => Array
(
[0] => brown
[1] => over the
[2] => dog
)
)
... and the text captured by the groups is now nicely arranged.
See this code run on eval.in and PHP Live Regex.
When working with stuff like this, don't forget about escaping:
<?php
ob_start();
?>
The quick __("brown ") fox jumps __( 'over the' ) lazy __("dog").
And __("everyone says \"hi\"").
<?php
$content = ob_get_clean();
$re = <<<RE
/__ \(
\s*
" ( (?: \\\\. | [^"])+ ) "
|
' ( (?: \\\\. | [^'])+ ) '
\s*
\)
/x
RE;
preg_match_all($re, $content, $matches, PREG_SET_ORDER);
foreach($matches as $match)
echo end($match), "\n";
How about this:
(__(\('[^']+'\)|\("[^"]+"\)))
Instead of the non greedy ., use any char but the quotes [^'] or [^"]
Enclose double and single quotes with square brackets as a character class:
$str = 'The quick __( "brown ") fox jumps __(\'over the\') lazy __("dog")';
preg_match_all("/__\(\s*([\"']).*?\\1\s*\)/ium", $str, $matches);
echo '<pre>';
var_dump($matches[0]);
// the output:
array (size=3)
0 => string '__( "brown ")'
1 => string '__('over the')'
2 => string '__("dog")'
And here is example with the same solution on phpliveregex.com:
http://www.phpliveregex.com/p/exF
(section preg_match_all)

Split a string while keeping delimiters and string outside

I'm trying to do something that must be really simple, but I'm fairly new to PHP and I'm struggling with this one. What I want is to split a string containing 0, 1 or more delimiters (braces), while keeping the delimiters AND the string between AND the string outside.
ex: 'Hello {F}{N}, how are you?' would output :
Array ( [0] => Hello
[1] => {F}
[2] => {N}
[3] => , how are you? )
Here's my code so far:
$value = 'Hello {F}{N}, how are you?';
$array= preg_split('/[\{\}]/', $value,-1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
print_r($array);
which outputs (missing braces) :
Array ( [0] => Hello
[1] => F
[2] => N
[3] => , how are you? )
I also tried :
preg_match_all('/\{[^}]+\}/', $myValue, $array);
Which outputs (braces are there, but the text outside is flushed) :
Array ( [0] => {F}
[1] => {N} )
I'm pretty sure I'm on the good track with preg_split, but with the wrong regex. Can anyone help me with this? Or tell me if I'm way off?
You aren't capturing the delimiters. Add them to a capturing group:
/(\{.*?\})/
You need parentheses around the part of the expression to be captured:
preg_split('/(\{[^}]+\})/', $myValue, -1, PREG_SPLIT_DELIM_CAPTURE);
See the documentation for preg_split().

string to array, split by single and double quotes, ignoring escaped quotes

i have another php preg_split question which is very similar to my last question, although i fear the solution will be quite a bit more complicated. as before, i'm trying to use php to split a string into array components using either " or ' as the delimiter. however in addition to this i would like to ignore escaped single quotations within the string (escaped double quotations within a string will not happen so there is no need to worry about that). all of the examples from my last question remain valid, but in addition the following two desired results should also be obtained:
$pattern = "?????";
$str = "the 'cat\'s dad sat on' the mat then \"fell 'sideways' off\" the mat";
$res = preg_split($pattern, $str, null, PREG_SPLIT_DELIM_CAPTURE);
print_r($res);
/*output:
Array
(
[0] => the
[1] => 'cat\'s dad sat on'
[2] => the mat then
[3] => "fell 'sideways' off"
[4] => the mat
)*/
$str = "the \"cat\'s dad\" sat on 'the \"cat\'s\" own' mat";
$res = preg_split($pattern, $str, null, PREG_SPLIT_DELIM_CAPTURE);
print_r($res);
/*output:
Array
(
[0] => the
[1] => "cat\'s dad"
[2] => sat on
[3] => 'the "cat\'s" own'
[4] => mat
)*/
#mcrumley's answer to my previous question worked well if there were no escaped quotations:
$pattern = "/('[^']*'|\"[^\"]*\")/U";
however as soon as an escaped single quotation is given the regex uses it as the end of the match, which is not what i want.
i have tried something like this:
$pattern = "/('(?<=(?!\\').*)'|\"(?<=(?!\\').*)\")/";
but its not working. unfortunately my knowledge of lookarounds is not good enough for this.
after some reading and fiddling...
this seems closer:
$pattern = "/('(?:(?!\\').*)')|(\"(?:(?!\\'|').*)\")/";
but the level of greedyness is wrong and does not produce the above outputs.
Try this:
$pattern = "/(?<!\\\\)('(?:\\\\'|[^'])*'|\"(?:\\\\\"|[^\"])*\")/";
^^^^^^^^^ ^^^^^^^^^ ^ ^^^^^^^^^^ ^
Demo at http://rubular.com/r/Eps2mx8KCw.
You can also collapse that into a unified expression using back-references:
$pattern = "/(?<!\\\\)((['\"])(?:\\\\\\2|(?!\\2).)*\\2)/";
Demo at http://rubular.com/r/NLZKyr9xLk.
These don't work though if you also want escaped backslashes to be recognized in your text, but I doubt that's a scenario you need to account for.

PHP preg_split with two delimiters unless a delimiter is within quotes

Further on from my previous question about preg_split which was answers super fast, thanks to nick; I would really like to extend the scenario to no split the string when a delimiter is within quotes. For example:
If I have the string foo = bar AND bar=foo OR foobar="foo bar", I'd wish to split the sting on every space or = character but include the = character in the returned array (which works great currently), but I don't want to split the string either of the delimiters are within quotes.
I've got this so far:
<!doctype html>
<?php
$string = 'foo = bar AND bar=foo';
$array = preg_split('/ +|(=)/', $string, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
?>
<pre>
<?php
print_r($array);
?>
</pre>
Which gets me:
Array
(
[0] => foo
[1] => =
[2] => bar
[3] => AND
[4] => bar
[5] => =
[6] => foo
)
But if I changed the string to:
$string = 'foo = bar AND bar=foo OR foobar = "foo bar"';
I'd really like the array to be:
Array
(
[0] => foo
[1] => =
[2] => bar
[3] => AND
[4] => bar
[5] => =
[6] => foo
[6] => OR
[6] => foobar
[6] => =
[6] => "foo bar"
)
Notice the "foo bar" wasn't split on the space because it's in quotes?
Really not sure how to do this within the RegEx or if there is even a better way but all your help would be very much appreciated!
Thank you all in advance!
Try
$array = preg_split('/(?: +|(=))(?=(?:[^"]*"[^"]*")*[^"]*$)/', $string, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
The
(?=(?:[^"]*"[^"]*")*[^"]*$)
part is a lookahead assertion making sure that there is an even number of quote characters ahead in the string, therefore it will fail if the current position is between quotes:
(?= # Assert that the following can be matched:
(?: # A group containing...
[^"]*" # any number of non-quote characters followed by one quote
[^"]*" # the same (to ensure an even number of quotes)
)* # ...repeated zero or more times,
[^"]* # followed by any number of non-quotes
$ # until the end of the string
)
I was able to do this by adding quoted strings as a delimiter a-la
"(.*?)"| +|(=)
The quoted part will be captured. It seems like this is a bit tenuous and I did not test it extensively, but it at least works on your example.
But why bother splitting?
After a look at this old question, this simple solution comes to mind, using a preg_match_all rather than a preg_split. We can use this simple regex to specify what we want:
"[^"]*"|\b\w+\b|=
See online demo.

return empty string from preg_split

Right now i'm trying to get this:
Array
(
[0] => hello
[1] =>
[2] => goodbye
)
Where index 1 is the empty string.
$toBeSplit= 'hello,,goodbye';
$textSplitted = preg_split('/[,]+/', $toBeSplit, -1);
$textSplitted looks like this:
Array
(
[0] => hello
[1] => goodbye
)
I'm using PHP 5.3.2
[,]+ means one or more comma characters while as much as possible is matched. Use just /,/ and it works:
$textSplitted = preg_split('/,/', $toBeSplit, -1);
But you don’t even need regular expression:
$textSplitted = explode(',', $toBeSplit);
How about this:
$textSplitted = preg_split('/,/', $toBeSplit, -1);
Your split regex was grabbing all the commas, not just one.
Your pattern splits the text using a sequence of commas as separator (its syntax also isn't perfect, as you're using a character class for no reason), so two (or two hundred) commas count just as one.
Anyway, since your just using a literal character as separator, use explode():
$str = 'hello,,goodbye';
print_r(explode(',', $str));
output:
Array
(
[0] => hello
[1] =>
[2] => goodbye
)

Categories