Unable to extract value between two characters - php

I'm trying with so many examples online to get the numbers between a _ and .. It simple outputs empty string for whatever reason.
String:
/chat_3.txt
I want to be able to extract the number in it, which is 3 in the above string.How do I do that?
I tried as below, but it gives empty output:
$s = '/chat_3.txt';
$matches = array();
$t = preg_match('/_(.*?)\./s', $s, $matches);
Then, I write the output into a file in Joomla like this:
$file = __DIR__ . '/file.txt';
JFile::write($file, $matches[1]);
EDIT:
In fact, I passed array instead of string.

So, the real issue is that
I passed array instead of string
If you need to extract the digits from the last occurrence of .+digits+., you can easily achieve that with a preg_filter function:
$s = array('/chat_3.txt', '/chat_old.txt', '/chat_15.txt');
$matches = array();
$t = preg_filter('/.*_(\d+)\..*/s', '$1',$s);
print_r($t);
See the PHP demo
The preg_filter will return only those values where it found a match. The replaced values will be returned. So, .*_(\d+)\..* will match any 0+ chars as many as possible up to the last _ + 1 or more digits (captured into Group 1) + . + any zero or more chars up to the end of string, and will replace all this with the digits found in Group 1.

Related

PHP: What is the pattern to find numbers-dashed sub-strings and replace after modification?

I have strings with numbers-dashes sub-strings. I want to find these sub-strings and replace them after some modifications.
For example, the string is like:
This is the string number 123-45-6789-0 which contains 12-34567.
Now I want to find sub-strings of numbers-dashes (123-45-6789-0 and 12-34567) and replace them with the modified sub-strings. For example the final string would be like this:
This is the modified string number 0-6789-45-123 which contains 34567-12.
I have already tried preg_match_all(string $pattern, string $subject, array &$matches) with:
$pattern = '/-*\d+-*/';
but it gives me an array of numbers each one with a dash, like this:
$matches = [123-, 45-, 6789-, 0, 12-, 34567]
whereas, I want an array of two sub-strings, like this:
$matches = [0 => 123-45-6789-0, 1 => 12-34567]
in order to do modifications and replacements (using str_replace()), separately.
Which pattern and methods should I use for these purpose?
Thanks in advance.
You may use \d+(?:-\d+)+ regex with a preg_replace_callback` function:
$str = 'This is the string number 123-45-6789-0 which contains 12-34567.';
echo preg_replace_callback('~\d+(?:-\d+)+~', function($m) {
return implode('-', array_reverse(explode('-', $m[0]))); }
,$str);
// => This is the string number 0-6789-45-123 which contains 34567-12.
See PHP demo and the regex demo.
The \d+(?:-\d+)+ pattern matches
\d+ - 1+ digits
(?:-\d+)+ - 1 or more occurrences of - and 1+ digits sequences.
$m is a match array, $m[0] holds the match value. With explode, the string is split with -, then the array is reversed, and then joined back with implode.

Return xx characters before a certain string

So I have this string:
Best location using 168 cars + cleaning
The '168' is the part i'd like to extract from this string.
I have approximately 80 occurences of this string, all alternating from 'xx' cars to 'xxx' cars (so, 2 or 3 numbers). However, in each string, 'cars' comes after the number i'd like to return.
What would be the best way using PHP to achieve this?
The best way is to do a simple preg_match on the text.
See the tutorial: http://php.net/manual/en/function.preg-match.php
<?php
$string = 'Best location using 168 cars + cleaning';
$pattern = '/(\d{2,3}+) cars/';
preg_match($pattern, $string, $match);
echo $match[1];
This regex returns all the numbers with length of 2 to 3 before the word cars.
you can change the length as you want and \d means all the numbers.
Easiest way is probably via preg_match(). Look for a space, one or more digits, a space, then the word cars. Use parens to capture the digits. That gives you pattern like this:
' (\d+) cars'
Then just pass that to preg_match() with a third argument to capture the parenthesized substring:
if (preg_match('/ (\d+) cars/', $str, $match)) {
echo "your num is: " . $match[1] . "\n";
}
Note this will also capture 1 cars and 1234 cars. If that's a problem, and you want to ensure that you only get the values with two or three digits, you can tweak the pattern to explicitly require that:
' (\d{2,3}) cars'
I would explode the string on a space and then loop through the array looking for the string "cars" and then get the key value for this. From here you know that the number will be before the "cars" occurrence so minus 1 from this key value and look in the array.
$original_string = "Best location using 168 cars + cleaning";
$string = explode(" ", $original_string);
foreach ($string as $key => $part) {
if($part == "cars") {
$number = $string[$key-1];
}
}
Explanation:
$original_string is whatever your whole string where the number is unknown.
$string is an array of the $original_string, each word will be in it's own part of the array
we loop through this array looking for the string "cars" and also get its key value.
If we find it successfully we then go to the key value minus one to find the number. We do this because we know the number appears before the "cars" string.

PHP regex for math operations

So i'm trying to create a regex without success.
This is what i get as in input string:
String A: "##(ABC 50a- {+} UDF 69,22g,-) {*} 3##"
String B: "##ABC 0,10,- DEF {/} 9 ABC {*} UHG 3-##"
And this is what i need processed out of the regex:
Result A: "(50+69,22)*3"
String B: "0,10/9*3"
I just can't get the number replacement combined with the operation symbols.
This is what i got:
'/[^0-9\+\-\*\/\(\)\.]/'
Thankful for every help.
One simple solution consists of getting rid of everything you don't want.
So replace this:
\{(.+?)\}|[^0-9,{}()]+|(?<!\d),|,(?!\d)
With $1.
Simple enough:
$input = "(ABC 50a- {+} UDF 69,22g,-) {*} 3";
$output = preg_replace('#\{(.+?)\}|[^0-9,{}()]+|(?<!\d),|,(?!\d)#', '$1', $input);
\{(.+?)\} part matches everything inside {...} and outputs it (it gets replaced by $1)
[^0-9,{}()]+ gets rid of every character not belonging to the ones we're trying to keep (it's replaced with an empty string)
(?<!\d),|,(?!\d) throws out commas which are not part of a number
Unfortunately, I can't say much else without a better spec.
A good start would be to write down in words the patterns that you want to match. For instance, you've said that you know the operations are inside {}, but that doesn't appear anywhere in your first attempt at a regex.
You can also break it down into separate sections, and then build it up later. So for instance you might say:
if you see parentheses, keep them in the final answer
a number is made up either of digits...
...or digits followed by a comma and more digits
an operation is always in curly braces, and is either +, -, *, or /
everything else should be thrown away
Given the above list:
matching parentheses is easy: [()]
matching a digit can be done with [0-9] or \d; at least one is +; so "digits" is \d+
comma digits is easy: ,\d+; make it optional with ?and you get \d+(,\d+)?
any of four operations is just [+*/-]; escape the / and - to get [+*\/\-] don't forget that { and } have special meanings in regexes, so need to be escaped as \{ and \}; our list of operations in braces becomes: \{[+*\/\-]\}
Now we have to put it together; one way would be to use preg_match_all to find all occurences of any of those patterns, in order, and then we can stick them back together. So our regex is just "this or this or this or this":
/[()]|\d+(,\d+)?|\{[+*\/\-]\}/
I haven't tested this, but given the explanation of how I arrived at it, hopefully you can figure out how to test parts of it and tweak it if necessary.
I`m not good at regex but I found another approach:
Do EXTRA check of input before running eval!!!
$string = "(ABC 50a- {+} UDF 69,22g) {*} 3";
$new ='';
$string = str_split($string);
foreach($string as $char) {
if(!ctype_alnum($char) || ctype_digit($char) ){
//you don't want letters, except symbols like {, ( etc
$new .=$char;
}
}
//echo $new; will output -> ( 50- {+} 69,22) {*} 3
//remove the brackets although you could put it in the if statement ...
$new = str_replace(array('{','}'),array('',''), $new);
//floating point numbers use dot not comma
$new = str_replace(',','.', $new);
$p = eval('return '.$new.';');
print $p; // -57.66
Used: ctype_digit, ctype_alnum, eval, str_split, str_replace
P.S: I assumed that the minus before the base operation is taken into account.
Just a quick try before leaving the office ;-)
$data = array(
"(ABC 50a- {+} UDF 69,22g) {*} 3",
"ABC 0,10- DEF {/} 9 ABC {*} UHG 3-"
);
foreach($data as $d) {
echo $d . " = " . extractFormula($d) . "\n";
}
function extractFormula($string) {
$regex = '/([()])|([0-9]+(,[0-9]+)?)|\{([+\*\/-])\}/';
preg_match_all($regex, $string, $matches);
$formula = implode(' ', $matches[0]);
$formula = str_replace(array('{', '}'),NULL,$formula);
return $formula;
}
Output:
(ABC 50a- {+} UDF 69,22g) {*} 3 = ( 50 + 69,22 ) * 3
ABC 0,10- DEF {/} 9 ABC {*} UHG 3- = 0,10 / 9 * 3
If some one likes to fiddle around with the code, here is a live example: http://sandbox.onlinephpfunctions.com/code/373d76a9c0948314c1d164a555bed847f1a1ed0d

preg_split String into Array at First Non-Numeric Character

I have a string with some numbers and text and I'm trying to split the string at the first non-numeric character.
For Example, I have a few strings like
$value = '150px';
$value = '50em';
$value = '25%';
I've been trying to split the string using preg_split and a little regex.
$value_split = preg_split( '/[a-zA-Z]/' , $fd['yks-mc-form-padding'] );
I'm able to get the first part of the string using $value_split[0], for example I can store 150, or 50 or 25. I need to return the second part of the string as well (px, em or %).
How can I split the string using preg_split or something similar to return both parts of the array??
Thanks!
If you want to use regex and you haven't already, you should play with RegExr.
To do what you're wanting with regex, assuming all the strings will be all numeric together, followed by all non-numeric, you could do:
$matches = array();
preg_match('/([0-9]+)([^0-9]+)/',$value,$matches);
Then $matches[1] will be the numeric part and $matches[2] will be the rest
To break it down,
[0-9] matches any numeric character, so [0-9]+ matches 1 or more numeric characters in a row, so per the docs $matches[1] will have the (numeric) text matched in by the first set of parentheses
and [^0-9] matches any non-numeric character, so [^0-9]+ matches 1 or more non-numeric characters in a row and fills $matches[2] because it's in the 2nd set of parentheses
By preg_split() you cannot achieve what are you trying to. It will delete the part of your string which separates the whole string (in this case it will be separated by character [a-zA-Z]). Use preg_match() (or preg_match_all()) function.
You can use this pattern:
/([0-9]+)([a-zA-Z%]+)/
See demo.
Use the PREG_SPLIT_OFFSET_CAPTURE flag - it will cause an array to be returned, with item [0] being the string matched, and item [1] its starting position in the original string.
You can then use that info to extract the rest of the string by using ordinary sub-string functionality.
Something along the lines of:
$values_split = preg_split( '/[a-zA-Z]/' , $fd['yks-mc-form-padding'] );
$position = $values_split[0][1]
$length = $values_split[0][0]
$startPos = $position + $length
$numToGet = lenght($input) - $startPos
$remainder = substr($inline, startPos, $numToGet)

Identifying a random repeating pattern in a structured text string

I have a string that has the following structure:
ABC_ABC_PQR_XYZ
Where PQR has the structure:
ABC+JKL
and
ABC itself is a string that can contain alphanumeric characters and a few other characters like "_", "-", "+", "." and follows no set structure:
eg.qWe_rtY-asdf or pkl123
so, in effect, the string can look like this:
qWe_rtY-asdf_qWe_rtY-asdf_qWe_rtY-asdf+JKL_XYZ
My goal is to find out what string constitutes ABC.
I was initially just using
$arrString = explode("_",$string);
to return $arrString[0] before I was made aware that ABC ($arrString[0]) itself can contain underscores, thus rendering it incorrect.
My next attempt was exlpoding it on "_" anyway and then comparing each of the exploded string parts with the first string part until I get a semblance of a pattern:
function getPatternABC($string)
{
$count = 0;
$pattern ="";
$arrString = explode("_", $string);
foreach($arrString as $expString)
{
if(strcmp($expString,$arrString[0])!==0 || $count==0)
{
$pattern = $pattern ."_". $arrString[$count];
$count++;
}
else break;
}
return substr($pattern,1);
}
This works great - but I wanted to know if there was a more elegant way of doing this using regular expressions?
Here is the regex solution:
'^([a-zA-Z0-9_+-]+)_\1_\1\+'
What this does is match (starting from the beginning of the string) the longest possible sequence consisting of the characters inside the square brackets (edit that per your spec). The sequence must appear exactly twice, each time followed by an underscore, and then must appear once more followed by a plus sign (this is actually the first half of PQR with the delimiter before JKL). The rest of the input is ignored.
You will find ABC captured as capture group 1.
So:
$input = 'qWe_rtY-asdf_qWe_rtY-asdf_qWe_rtY-asdf+JKL_XYZ';
$result = preg_match('/^([a-zA-Z0-9_+-]+)_\1_\1\+/', $input, $matches);
if ($result) {
echo $matches[2];
}
See it in action.
Sure, just make a regular expression that matches your pattern. In this case, something like this:
preg_match('/^([a-zA-Z0-9_+.-]+)_\1_\1\+JKL_XYZ$/', $string, $match);
Your ABC is in $match[1].
If the presence of underscores in these strings has a low frequency, it may be worth checking to see if a simple explode() will do it before bothering with regex.
<?php
$str = 'ABC_ABC_PQR_XYZ';
if(substr_count($str, '_') == 3)
$abc = reset(explode('_', $str));
else
$abc = regexy_function($str);
?>

Categories