I'm facing an issue with a function that gets a string between two other strings.
function string_between($str, $starting_word, $ending_word) {
$subtring_start = strpos($str, $starting_word);
$subtring_start += strlen($starting_word);
foreach ($ending_word as $a){
$size = strpos($str, $a, $subtring_start) - $subtring_start;
}
return substr($str, $subtring_start, $size);
}
The issue is that the function searches for the first ending_word in the array.
An example will be easier to understand:
$array_a = ['the', 'amen']; // Starting strings
$array_b = [',', '.']; // Ending strings
$str = "Hello, the world. Then, it is over.";
Expected result:
"the world."
Current result:
"the world. Then,"
The function will think that the ending_word is "," because it is the first element met in the array_b. However, the text encounters first the '.' after the "the" starting word.
How can I make sure the function goes through the text and stops at the first element in the $str present in the array_b, whatever the position in the array?
Any idea?
Basically, you need to break outside of your foreach loop when $size > 0
That way it stops looping through your array when it finds the 1st occurrence. Here is the more complete code with other fixes:
function stringBetween($string, $startingWords, $endingWords) {
foreach ($startingWords as $startingWord) {
$subtringStart = strpos($string, $startingWord);
if ($subtringStart > 0) {
foreach ($endingWords as $endingWord){
$size = strpos($string, $endingWord, $subtringStart) - $subtringStart + strlen($endingWord);
if ($size > 0) {
break;
}
}
if ($size > 0) {
return substr($string, $subtringStart, $size);
}
}
}
return null;
}
$startArr = array('the', 'amen'); // Starting strings
$endArr = array('.', ','); // Ending strings
$str = "Hello, the world. Then, it is over.";
echo stringBetween($str, $startArr, $endArr); // the world.
This type of problems are best solved by PCRE regexes, only couple of lines needed in function :
function string_between($str, $starts, $ends) {
preg_match("/(?:{$starts}).*?(?:{$ends})/mi", $str, $m);
return $m[0];
}
Then calling like this :
echo string_between("Hello, the world. Then, it is over.", 'the|amen', ',|\.');
Produces : the world.
The trick,- search to the nearest matching ending symbol is done with regex non-greedy seach, indicated by question symbol in pattern .*?. You can even extend this function to accept arrays as starting/ending symbols, just that case modify function (possibly with implode('|',$arr)) for concatenating symbols into regex grouping formula.
Edited version
This works now. Iterate over your teststrings from first array looking for position of occurance from teststring. If found one then search for the second teststring at startposition from end of first string.
To get the shortest hit I store the position from the second and take the minimum.
You can try it at http://sandbox.onlinephpfunctions.com/code/0f1e5c97da62b4daaf0e49f52271fe288d1cacbb
$array_a =array('the','amen');
$array_b =array(',','.', '#');
$str = "Hello, the world. Then, it is over.";
function earchString($str, $array_a, $array_b) {
forEach($array_a as $test) {
$pos = strpos($str, $test);
if ($pos===false) continue;
$found = [];
forEach($array_b as $test2) {
$posStart = $pos+strlen($test);
$pos2 = strpos($str, $test2, $posStart);
$found[] = ($pos2!==false) ? $pos2 : INF;
}
$min = min($found);
if ($min !== INF)
return substr($str,$pos,$min-$pos) .$str[$min];
}
return '';
}
echo earchString($str, $array_a, $array_b);
Related
I have this code:
$getClass = $params->get('pageclass_sfx');
var_dump($getClass); die();
The code above returns this:
string(24) "sl-articulo sl-categoria"
How can I retrieve the specific word I want without mattering its position?
Ive seen people use arrays for this but that would depend on the position (I think) that you enter these strings and these positions may vary.
For example:
$myvalue = $params->get('pageclass_sfx');
$arr = explode(' ',trim($myvalue));
echo $arr[0];
$arr[0] would return: sl-articulo
$arr[1] would return: sl-categoria
Thanks.
You can use substr for that in combination with strpos:
http://nl1.php.net/substr
http://nl1.php.net/strpos
$word = 'sl-categoria';
$page_class_sfx = $params->get('page_class_sfx');
if (false !== ($pos = strpos($page_class_sfx, $word))) {
// stupid because you already have the word... But this is what you request if I understand correctly
echo 'found: ' . substr($page_class_sfx, $pos, strlen($word));
}
Not sure if you want to get a word from the string if you already know the word... You want to know if it's there? false !== strpos($page_class_sfx, $word) would be enough.
If you know exactly what strings you're looking for, then stripos() should be sufficient (or strpos() if you need case-sensitivity). For example:
$myvalue = $params->get('pageclass_sfx');
$pos = stripos($myvalue, "sl-articulo");
if ($pos === FALSE) {
// string "sl-articulo" was not found
} else {
// string "sl-articulo" was found at character position $pos
}
If you need to check if some word are in string you may use preg_match function.
if (preg_match('/some-word/', 'many some-words')) {
echo 'some-word';
}
But this solution can be used for a small list of needed words.
For other cases i suggest you to use some of this.
$myvalue = $params->get('pageclass_sfx');
$arr = explode(' ',trim($myvalue));
$result = array();
foreach($arr as $key=> $value) {
// This will calculates all data in string.
if (!isset($result[$value])) {
$result[$value] = array(); // or 0 if you don`t need to use positions
}
$result[$value][] = $key; // For all positions
// $result[$value] ++; // For count of this word in string
}
// You can just test some words like follow:
if (isset($result['sl-categoria'])) {
var_dump($result['sl-categoria']);
}
I would like to search for a substring in php so that it will be at the end of the given string.
Eg
on string 'abd def' if I search for def it would be at the end, so return true. But if I search for abd it will return false since it is not at the end.
Is it possible?
You could use preg_match for this:
$str = 'abd def';
$result = (preg_match("/def$/", $str) === 1);
var_dump($result);
An alternative way to do it which does not require splitting by a separator or regular expressions. This tests whether the last x characters equal the test string, where x equals the length of the test string:
$string = "abcdef";
$test = "def";
if(substr($string, -(strlen($test))) === $test)
{
/* logic here */
}
Assuming whole words:
$match = 'def';
$words = explode(' ', 'abd def');
if (array_pop($words) == $match) {
...
}
Or using a regex:
if (preg_match('/def$/', 'abd def')) {
...
}
This answer should be fully robust regardless of full words or anything else
$match = 'def';
$words = 'abd def';
$location = strrpos($words, $match); // Find the rightmost location of $match
$matchlength = strlen($match); // How long is $match
/* If the rightmost location + the length of what's being matched
* is equal to the length of what's being searched,
* then it's at the end of the string
*/
if ($location + $matchlength == strlen($words)) {
...
}
Please look strrchr() function. Try like this
$word = 'abcdef';
$niddle = 'def';
if (strrchr($word, $niddle) == $niddle) {
echo 'true';
} else {
echo 'false';
}
I would need to reduce the quantity of these numbers and present them in a more concise way, instead of presenting several lines of numbers with the same "prefix" or "root". For example:
If I have an array like this, with several strings of numbers (obs: only numbers and the array is already sorted):
$array = array(
"12345647",
"12345648",
"12345649",
"12345657",
"12345658",
"12345659",
);
The string: 123456 is the same in all elements of the array, so it would be the root or the prefix of the number. According to the above array I would get a result like this:
//The numbers in brackets represent the sequence of the following numbers,
//instead of showing the rows, I present all the above numbers in just one row:
$stringFormed = "123456[4-5][7-9]";
Another example:
$array2 = array(
"1234",
"1235",
"1236",
"1247",
"2310",
"2311",
);
From the second array, I should get a result like this:
$stringFormed1 = "123[4-7]";
$stringFormed2 = "1247";
$stringFormed3 = "231[0-1]";
Any idea?
$array = array(
"12345647",
"12345648",
"12345649",
"12345657",
"12345658",
"12345659",
);
//find common string positions for all elements
$res = array();
foreach($array as $arr){
for($i=0;$i<strlen($arr);$i++){
$res[$i][$arr[$i]] = $arr[$i];
}
}
//make final string
foreach($res as $pos){
if(count($pos)==1)
$str .= implode('',$pos);
else{
//u may need to sort these values if you want them in order
$end = end($pos);
$first = reset($pos);
$str .="[$first-$end]";
}
}
echo $str; // "123456[4-5][7-9]";
Well, as I understand you want the final string with unique characters. (i'm not sure if you want it ordered)
So, first implode to create the string
$stringFormed = implode("", $array);
Then we get the unique chars :
$stringFormed=implode("",array_unique(str_split($stringFormed)));
OUTPUT: 123456789
That as a solution for first example but i didn't thought there could be several roots.
By the way i'm not sure it's well coded...
<?php
function longest_common_substring($words)
{
$words = array_map('strtolower', array_map('trim', $words));
$sort_by_strlen = create_function('$a, $b', 'if (strlen($a) == strlen($b)) { return strcmp($a, $b); } return (strlen($a) < strlen($b)) ? -1 : 1;');
usort($words, $sort_by_strlen);
// We have to assume that each string has something in common with the first
// string (post sort), we just need to figure out what the longest common
// string is. If any string DOES NOT have something in common with the first
// string, return false.
$longest_common_substring = array();
$shortest_string = str_split(array_shift($words));
while (sizeof($shortest_string)) {
array_unshift($longest_common_substring, '');
foreach ($shortest_string as $ci => $char) {
foreach ($words as $wi => $word) {
if (!strstr($word, $longest_common_substring[0] . $char)) {
// No match
break 2;
} // if
} // foreach
// we found the current char in each word, so add it to the first longest_common_substring element,
// then start checking again using the next char as well
$longest_common_substring[0].= $char;
} // foreach
// We've finished looping through the entire shortest_string.
// Remove the first char and start all over. Do this until there are no more
// chars to search on.
array_shift($shortest_string);
}
// If we made it here then we've run through everything
usort($longest_common_substring, $sort_by_strlen);
return array_pop($longest_common_substring);
}
$array = array(
"12345647",
"12345648",
"12345649",
"12345657",
"12345658",
"12345659",
);
$result= longest_common_substring($array);
for ($i = strlen($result); $i < strlen($array[0]); $i++) {
$min=intval($array[0][$i]);
$max=$min;
foreach ($array as $string) {
$val = intval($string[$i]);
if($val<$min)
$min=$val;
elseif($val>$max)
$max=$val;
}
$result.='['.$min.'-'.$max.']';
}
echo $result;
?>
So... I have a problem with counting all ccurrences of preg_match and summing/addition first parts of it (number before "/"). I just want to get the average of numbers before "/". Really sorry for bad english.
script.php
$wyniki=file("wyniki.txt");
foreach($wyniki as $w)
{
preg_match("/^([0-9]{1})\/([0-9]{1})$/",$w,$ar);
if(!empty($ar)){
print_r($ar[1].'/'.$ar[2]);
echo("\n");
}
}
script2.php (fail, but other way of script.php)
$file=fopen("wyniki.txt", "r");
$read=fread($file, filesize("wyniki.txt"));
echo($read."\n");
//if($read!=trim(''))
//{
preg_match("/^([0-9]{1})\/([0-9]{1})$/",$read,$ar);
//print_r($ar[1].'/'.$ar[2]);
print_r($ar);
echo("\n");
//}
fclose($file);
wyniki.txt
5/5
asd
4/5
fgh
Your regex seems ok. Perhaps you have some whitespace in the end of each line (you have used '$'). You can delete these by using trim(). Additionally, regex might not be the best solution in every case. Perhaps it would be simpler for you to test for the occurrence of '/', then use split() and check it the array[0] is a number (is_numeric()). Consider the following code:
$wyniki=file("wyniki.txt");
foreach($wyniki as $w) {
if (strpos($w, '/') !== FALSE) {
$tarr = split("\/", $w);
if (is_numeric($tarr[0]))
echo "This is a number: " . $tarr[0];
}
}
Second code with regex:
$flines = array("5/5","asd","4/5","fgh");
$regex = "#^([0-9]{1})\/([0-9]{1})$#";
$res = $tot = 0;
foreach ($flines as $fline) {
$arr = array();
preg_match($regex, $fline, $arr);
if (is_numeric($arr[1])) {
$res += $arr[1];
$tot++;
}
}
$avg = bcdiv($res, $tot, 2);
echo "Average: $avg";
$test = array('<h1>text1</h1>','<h1>text2<h1>','<h1>text3</h1><p>subtext3</p>');
In a long long texts, I use preg_split cut them into small pieces. I want to remove only h1 tag wraped and without hyperlink.
I hope remove all the text looks like: <h1>text1</h1> //only h1 wraped and without hyperlink.
And remain <h1>text2<h1>,<h1>text3</h1><p>subtext3</p>
Use a loop to go through each array element and find each instance of the string "<". Then look at the next 3 characters. If they're "h1>" then you you have the correct tag. If you ever find a "<" that has a different 3 characters, then its not an "" HTML tag and you can remove this array object.
To remove the given object from the array, you can use unset($array[$index]) and when you're done I recommend using a sort to remove any index skips that may occur.
You'll want to use functions such as strpos to get the position of a string, and substr to get a subset of the given string. php.net is your friend :)
Here is an example function which works with your $test array:
<?php
$test = array('<h1>text1</h1>','<h1>text2<h1>','<h1>text3</h1><p>subtext3</p>');
function removeBadElements(&$array) {
foreach($array as $k => $v) {
// $v is a single array element
$offset = 0;
do {
$pos = strpos($v, '<', $offset);
$offset = $pos + 1;
if($pos === false) { break; }
$tag = substr($v, $pos, 3);
$next = substr($v, $pos+1, 1);
if($next == '/') { continue; }
if($tag == '<h1') { continue; }
else {
unset($array[$k]);
break;
}
} while($offset + 2 < strlen($v));
}
}
echo "\nORIG ARRAY:\n";
print_r($test);
removeBadElements($test);
echo "\n\n-------\nMODIFIED ARRAY:\n\n";
print_r($test);
?>