REGEX for first two characters from a set of values? - php

I have a set of starting 2 alphabets as:
$arr = ['AB', 'DC', 'LF']
Problem: I need to make a regex (in PHP and TypeSript) that passes only those strings which starts with above values.
Example:
Valid:
ABwerty45^&*jk
ABwerrtty
LF%$^erftgt5234
Invalid:
TABYR56H
ab7877
Abtyu7
Any help is appreciated.

You could join() the array and compose a regex with an alternation like this:
<?php
$strings = <<<DATA
ABwerty45^&*jk
ABwerrtty
LF%$^erftgt5234
TABYR56H
ab7877
Abtyu7
DATA;
$arr = ['AB', 'DC', 'LF'];
$regex = '~^(?:' . join('|', $arr) . ').*~m';
if (preg_match_all($regex, $strings, $matches)) {
print_r($matches);
}
?>
Which yields
Array
(
[0] => Array
(
[0] => ABwerty45^&*jk
[1] => ABwerrtty
[2] => LF%$^erftgt5234
)
)
Basically, this says:
^ # match the start of the string
(?:AB|DC|LF) # AB or DC or LF
.* # 0+ characters in that line

Instead of a regex you could check if the first 2 characters are present in the array:
$arr = ['AB', 'DC', 'LF'];
if (in_array(substr("ABwerty45^&*jk",0, 2), $arr)) {
// ...
}
Demo
const strings = [
"ABwerty45^&*jk",
"ABwerrtty",
"LF%$^erftgt5234",
"TABYR56H",
"ab7877",
"Abtyu7"
];
const arr = ['AB', 'DC', 'LF'];
strings.forEach((s) => {
let match = arr.includes(s.substring(0, 2));
match ? console.log("Match : ", s) : console.log("No match: ", s);
});

Related

How to find specific number in a string php?

I have different strings in which I want to find specific number. If the number is within the string it should print that number.
Strings are like these:
string(9) "path_12.0"
string(9) "path_12.1"
string(9) "path_13.0"
string(9) "path_13.1"
string(9) "path_13.2"
Numbers are like:
int(12)
int(12)
int(13)
int(13)
int(13)
What I tried is:
if (strpos(','.$mainString.',' , ','.$QuestionId.',') != FALSE) {
echo $QuestionId;
} // this doesn't print anything in the body
I also tried the below trick but it also doesn't print anything
if(in_array($QuestionId, explode(',', $mainString))) {
echo $QuestionId;
}
I want to check something like this:
if($questionId is in $mainString) {
echo $questionId;
}
Note: I searched similar questions on StackOverflow but I didn't found a solution which solved my issue, therefore I'm posting this question.
Another option could be to create an array with your strings and use preg_grep with a pattern that checks if the first part of the decimal is equal of one of the numbers.
Example of a pattern where the digits from the array are used as an alternation:
_\K(?:12|13)(?=\.\d+)
_\K Match underscore and forget what was matched
(?: Non capturing group
12|13 Match either 12 or 13
) Close non capturing group
(?=\.\d+) Positive lookahead, assert what is directly on the right is a dot and 1+ digits
For example:
$numbers = [12, 13];
$strings = [
"path_12.0",
"path_12.1",
"path_13.0",
"path_13.1",
"path_13.2",
"path_14.1"
];
$pattern = "/_\K(?:" . implode('|', $numbers) . ")(?=\.\d+)/";
$resullt = preg_grep($pattern, $strings);
print_r($resullt);
Result
Array
(
[0] => path_12.0
[1] => path_12.1
[2] => path_13.0
[3] => path_13.1
[4] => path_13.2
)
Php demo
Or if you want to print the numbers only, you might use array_reduce and collect the matches:
$result = array_reduce($strings, function($carry, $item) use ($pattern){
if (preg_match($pattern, $item, $matches)){
$carry[] = $matches[0];
}
return $carry;
});
print_r($result);
Result
Array
(
[0] => 12
[1] => 12
[2] => 13
[3] => 13
[4] => 13
)
Php demo
You can use below snippet,
$paths = ["path_12.0", "path_12.1", "path_13.0", "path_13.1", "path_13.2", ];
$nos = [12, 12, 13, 13, 13, ];
function strpos_arr($needle,$haystack)
{
if (!is_array($haystack)) {
$haystack = [$haystack];
}
foreach ($haystack as $what) {
if (($pos = strpos($what,(string)$needle)) !== false) {
return $pos;
}
}
return false;
}
foreach ($nos as $key => $value) {
// checking if question id in in path array with str pos
if(strpos_arr($value,$paths) !== false){
echo $value."\n";
}
}
Demo.
$array_strings = ["path_12.0", "path_12.1", "path_13.0", "path_13.1", "path_13.2"];
$array_numbers = [12, 22, 13, 11, 17];
$results = [];
foreach ($array_strings as $string){
preg_match_all('!\d+\.*\d*!', $string, $matches);
foreach ($array_numbers as $number){
if (in_array($number, $matches[0])){
array_push($results, $number);
}
}
}
print_r($results);
results: Array ( [0] => 12 [1] => 13 )
Note 1: array answers can have duplicate values.

Text replace using array

I guys,
I would like replace a text adding specific links on specific words.
So, I created a dynamic array like this:
$replace = array (
"ferrari" => 'ferrari',
"ferrari 2" => 'ferrari 2'
etc.
)
I found a way to replace the text using:
preg_replace(array_keys($replace), array_values($replace), $subject)
but if in the $subject there is the string "ferrari 2" it will be replaced with
[ferrari] instead of [ferrari 2].
How can I do an exact match?
Thanks a lot!
You may use the following solution:
$s = "he has a ferrari and ferrari 2 and what not."; // SAMPLE STRING
$replace = array ( // REPLACEMENTS ARRAY
"ferrari" => 'ferrari',
"ferrari 2" => 'ferrari 2'
);
$keys = array_map('strlen', array_keys($replace)); // SORTING BY KEY LENGTH
array_multisort($keys, SORT_DESC, $replace); // IN DESCENDING ORDER
$pat = '~' . implode("|", array_keys($replace)) . '~'; // BUILDING A PATTERN
echo $pat . "\n"; // => ~ferrari 2|ferrari~ - matches either ferrari 2 or ferrari
$res = preg_replace_callback($pat, function($m) use ($replace) { // REPLACING...
return isset($replace[$m[0]]) ? $replace[$m[0]] : $m[0]; // IF THE KEY EXISTS,
}, $s); // REPLACE WITH VALUE, ELSE KEEP THE MATCH
echo $res; // => he has a ferrari and ferrari 2 and what not.
See the PHP demo
change the order to:
$replace = array (
"ferrari 2" => 'ferrari 2'
"ferrari" => 'ferrari',
)
Usually when you replace, just changing the order to where the smallest keys are last solves a lot.
I imagine that the var $subject is like this:
$subject = 'ferrari ferrari 2';
Try with this:
$replace = array (
"/ferrari(?!\s+\d+)/" => 'ferrari',
"/ferrari 2/" => 'ferrari 2'
);
echo preg_replace(array_keys($replace), array_values($replace), $subject);

use preg_split to split chords and words

I'm working on a little piece of code playing handling song tabs, but i'm stuck on a problem.
I need to parse each song tab line and to split it to get chunks of chords on the one hand, and words in the other.
Each chunk would be like :
$line_chunk = array(
0 => //part of line containing one or several chords
1 => //part of line containing words
);
They should stay "grouped". I mean by this that it should split only when the function reaches the "limit" between chords and words.
I guess I should use preg_split to achieve this. I made some tests, but I've been only able to split on chords, not "groups" of chords:
$line_chunks = preg_split('/(\[[^]]*\])/', $line, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
Those examples shows you what I would like to get :
on a line containing no chords :
$input = '{intro}';
$results = array(
array(
0 => null,
1 => '{intro}
)
);
on a line containing only chords :
$input = '[C#] [Fm] [C#] [Fm] [C#] [Fm]';
$results = array(
array(
0 => '[C#] [Fm] [C#] [Fm] [C#] [Fm]',
1 => null
)
);
on a line containing both :
$input = '[C#]I’m looking for [Fm]you [G#]';
$results = array(
array(
0 => '[C#]',
1 => 'I’m looking for'
),
array(
0 => '[Fm]',
1 => 'you '
),
array(
0 => '[G#]',
1 => null
),
);
Any ideas of how to do this ?
Thanks !
preg_split isn't the way to go. Most of the time, when you have a complicated split task to achieve, it's more easy to try to match what you are interested by instead of trying to split with a not easy to define separator.
A preg_match_all approach:
$pattern = '~ \h*
(?| # open a "branch reset group"
( \[ [^]]+ ] (?: \h* \[ [^]]+ ] )*+ ) # one or more chords in capture group 1
\h*
( [^[\n]* (?<=\S) ) # eventual lyrics (group 2)
| # OR
() # no chords (group 1)
( [^[\n]* [^\s[] ) # lyrics (group 2)
) # close the "branch reset group"
~x';
if (preg_match_all($pattern, $input, $matches, PREG_SET_ORDER)) {
$result = array_map(function($i) { return [$i[1], $i[2]]; }, $matches);
print_r($result);
}
demo
A branch reset group preserves the same group numbering for each branch.
Note: feel free to add:
if (empty($i[1])) $i[1] = null;
if (empty($i[2])) $i[2] = null;
in the map function if you want to obtain null items instead of empty items.
Note2: if you work line by line, you can remove the \n from the pattern.
I would go with PHP explode:
/*
* Process data
*/
$input = '[C#]I’m looking for [Fm]you [G#]';
$parts = explode("[", $input);
$results = array();
foreach ($parts as $item)
{
$pieces = explode("]", $item);
if (count($pieces) < 2)
{
$arrayitem = array( "Chord" => $pieces[0],
"Lyric" => "");
}
else
{
$arrayitem = array( "Chord" => $pieces[0],
"Lyric" => $pieces[1]);
}
$results[] = $arrayitem;
}
/*
* Echo results
*/
foreach ($results as $str)
{
echo "Chord: " . $str["Chord"];
echo "Lyric: " . $str["Lyric"];
}
Boudaries are not tested in the code, as well as remaining whitespaces, but it is a base to work on.

Regex Multiple Capture of Group

I'm using regex to capture the dimensions of ads
Source content is an HTML File, and I'm trying to capture for content that looks like:
size[200x400,300x1200] (could be 1-4 different sizes)
I'm trying to an array with the different sizes in it
My capture code looks like this:
$size_declaration = array();
$sizes = array();
$declaration_pattern = "/size\[(\d{2,4}x\d{2,4}|\d{2,4}x\d{2,4},){1,4}\]/";
$sizes_pattern = "/\d{2,4}x\d{2,4}/";
$result = preg_match($declaration_pattern, $html, $size_declaration);
if( $result ) {
$result = preg_match_all($sizes_pattern, $size_declaration[0], $sizes);
var_dump($sizes);
}
The code above produces usable results:
$sizes = array(
[0] => array (
[0] => '200x400',
[1] => '300x1200'
)
)
but it takes quite a bit of code. I was thinking it was possible to collect the results with a single regex, but I couldn't find a result that works. Is there a way to clean this up a bit?
It's not very practical to turn it into a single expression; it would be better to keep them separate; the first expression finds the boundaries and does rudimentary content checks on the inner contents, the second expression breaks it down into individual pieces:
if (preg_match_all('/size\[([\dx,]+)\]/', $html, $matches)) {
foreach ($matches[0] as $size_declaration) {
if (preg_match_all('/\d+x\d+/', $size_declaration, $sizes)) {
print_r($sizes[0]);
}
}
}
This one is a little simpler:
$html = "size[200x400,300x600,300x100]";
if (($result = preg_match_all("/(\d{2,4}x\d{2,4}){1,4}/", $html, $matches)) > 0)
var_dump($matches);
//
// $matches =>
// array(
// (int) 0 => array(
// (int) 0 => '200x400',
// (int) 1 => '300x600',
// (int) 2 => '300x100'
// ),
// (int) 1 => array(
// (int) 0 => '200x400',
// (int) 1 => '300x600',
// (int) 2 => '300x100'
// )
// )
//
The only way is to repeat the 4 eventual sizes in the pattern:
$subject = <<<LOD
size[523x800]
size[200x400,300x1200]
size[201x300,352x1200,123x456]
size[142x396,1444x32,143x89,231x456]
LOD;
$pattern = '`size\[(\d{2,4}x\d{2,4})(?:,(\d{2,4}x\d{2,4}))?(?:,(\d{2,4}x\d{2,4}))?(?:,(\d{2,4}x\d{2,4}))?]`';
preg_match_all($pattern, $subject, $matches, PREG_SET_ORDER);
foreach ($matches as &$match) { array_shift($match); }
print_r($matches);
The pattern can also be shorten using references to capture groups:
$pattern = '`size\[(\d{2,4}x\d{2,4})(?:,((?1)))?(?:,((?1)))?(?:,((?1)))?]`';
or with the Oniguruma syntax:
$pattern = '`size\[(\d{2,4}x\d{2,4})(?:,(\g<1>))?(?:,(\g<1>))?(?:,(\g<1>))?]`';

Turn text inside brackets to an array PHP

If I have a string that looks like this:
$myString = "[sometext][moretext][993][112]This is a long text";
I want it to be turned into:
$string = "This is a long text";
$arrayDigits[0] = 993;
$arrayDigits[1] = 112;
$arrayText[0] = "sometext";
$arrayText[1] = "moretext";
How can I do this with PHP?
I understand Regular Expressions is the solution. Please notice that $myString was just an example. There can be several brackets, not just two of each, as in my example.
Thanks for your help!
This is what I came up with.
<?php
#For better display
header("Content-Type: text/plain");
#The String
$myString = "[sometext][moretext][993][112]This is a long text";
#Initialize the array
$matches = array();
#Fill it with matches. It would populate $matches[1].
preg_match_all("|\[(.+?)\]|", $myString, $matches);
#Remove anything inside of square brackets, and assign to $string.
$string = preg_replace("|\[.+\]|", "", $myString);
#Display the results.
print_r($matches[1]);
print_r($string);
After that, you can iterate over the $matches array and check each value to assign it to a new array.
Try this:
$s = '[sometext][moretext][993][112]This is a long text';
preg_match_all('/\[(\w+)\]/', $s, $m);
$m[1] will contain all texts in the brakets, after this you could check type of each value. Also, you could check this using two preg_match_all: at first time with pattern /\[(\d+)\]/ (will return array of digits), in the second - pattern /\[([a-zA-z]+)\]/ (that will return words):
$s = '[sometext][moretext][993][112]This is a long text';
preg_match_all('/\[(\d+)\]/', $s, $matches);
$arrayOfDigits = $matches[1];
preg_match_all('/\[([a-zA-Z]+)\]/', $s, $matches);
$arrayOfWords = $matches[1];
For cases like yours you can make use of named subpatterns so to "tokenize" your string. With some little code, this can be made easily configurable with an array of tokens:
$subject = "[sometext][moretext][993][112]This is a long text";
$groups = array(
'digit' => '\[\d+]',
'text' => '\[\w+]',
'free' => '.+'
);
Each group contains the subpattern and it's name. They match in their order, so if the group digit matches, it won't give text a chance (which is necessary here because \d+ is a subset of \w+). This array can then turned into a full pattern:
foreach($groups as $name => &$subpattern)
$subpattern = sprintf('(?<%s>%s)', $name, $subpattern);
unset($subpattern);
$pattern = sprintf('/(?:%s)/', implode('|', $groups));
The pattern looks like this:
/(?:(?<digit>\[\d+])|(?<text>\[\w+])|(?<free>.+))/
Everything left to do is to execute it against your string, capture the matches and filter them for some normalized output:
if (preg_match_all($pattern, $subject, $matches))
{
$matches = array_intersect_key($matches, $groups);
$matches = array_map('array_filter', $matches);
$matches = array_map('array_values', $matches);
print_r($matches);
}
The matches are now nicely accessible in an array:
Array
(
[digit] => Array
(
[0] => [993]
[1] => [112]
)
[text] => Array
(
[0] => [sometext]
[1] => [moretext]
)
[free] => Array
(
[0] => This is a long text
)
)
The full example at once:
$subject = "[sometext][moretext][993][112]This is a long text";
$groups = array(
'digit' => '\[\d+]',
'text' => '\[\w+]',
'free' => '.+'
);
foreach($groups as $name => &$subpattern)
$subpattern = sprintf('(?<%s>%s)', $name, $subpattern);
unset($subpattern);
$pattern = sprintf('/(?:%s)/', implode('|', $groups));
if (preg_match_all($pattern, $subject, $matches))
{
$matches = array_intersect_key($matches, $groups);
$matches = array_map('array_filter', $matches);
$matches = array_map('array_values', $matches);
print_r($matches);
}
You could try something along the lines of:
<?php
function parseString($string) {
// identify data in brackets
static $pattern = '#(?:\[)([^\[\]]+)(?:\])#';
// result container
$t = array(
'string' => null,
'digits' => array(),
'text' => array(),
);
$t['string'] = preg_replace_callback($pattern, function($m) use(&$t) {
// shove matched string into digits/text groups
$t[is_numeric($m[1]) ? 'digits' : 'text'][] = $m[1];
// remove the brackets from the text
return '';
}, $string);
return $t;
}
$string = "[sometext][moretext][993][112]This is a long text";
$result = parseString($string);
var_dump($result);
/*
$result === array(
"string" => "This is a long text",
"digits" => array(
993,
112,
),
"text" => array(
"sometext",
"moretext",
),
);
*/
(PHP5.3 - using closures)

Categories