I want to get the position of a sub string from a string using php. I can use strpos(), but it returns only first occurrence. How can I get the positions of multiple occurrence.
From: http://www.php.net/manual/en/function.strpos.php#108426
function strpos_r($haystack, $needle)
{
if(strlen($needle) > strlen($haystack))
trigger_error(sprintf("%s: length of argument 2 must be <= argument 1", __FUNCTION__), E_USER_WARNING);
$seeks = array();
while($seek = strrpos($haystack, $needle))
{
array_push($seeks, $seek);
$haystack = substr($haystack, 0, $seek);
}
return $seeks;
}
This will return an array with the position of occurences.
From the manual, there are such functions in the comments.
function strpos_recursive($haystack, $needle, $offset = 0, &$results = array()) {
$offset = strpos($haystack, $needle, $offset);
if($offset === false) {
return $results;
} else {
$results[] = $offset;
return strpos_recursive($haystack, $needle, ($offset + 1), $results);
}
}
Third parameter of strpos, has a $offset you can use:
$positions_of_string = array();
$str_to_find = "string to find";
$str_length = strlen( $str_to_find );
$last_found = 0 - $str_length;
while( false !== $last_found ) {
$last_found = strpos( $the_string, $str_to_find, $last_found+$str_length );
if( false !== $last_found )
$positions_of_strings[] = $last_found;
}
Related
I need to parse an HTML document and to find all occurrences of string asdf in it.
I currently have the HTML loaded into a string variable. I would just like the character position so I can loop through the list to return some data after the string.
The strpos function only returns the first occurrence. How about returning all of them?
Without using regex, something like this should work for returning the string positions:
$html = "dddasdfdddasdffff";
$needle = "asdf";
$lastPos = 0;
$positions = array();
while (($lastPos = strpos($html, $needle, $lastPos))!== false) {
$positions[] = $lastPos;
$lastPos = $lastPos + strlen($needle);
}
// Displays 3 and 10
foreach ($positions as $value) {
echo $value ."<br />";
}
You can call the strpos function repeatedly until a match is not found. You must specify the offset parameter.
Note: in the following example, the search continues from the next character instead of from the end of previous match. According to this function, aaaa contains three occurrences of the substring aa, not two.
function strpos_all($haystack, $needle) {
$offset = 0;
$allpos = array();
while (($pos = strpos($haystack, $needle, $offset)) !== FALSE) {
$offset = $pos + 1;
$allpos[] = $pos;
}
return $allpos;
}
print_r(strpos_all("aaa bbb aaa bbb aaa bbb", "aa"));
Output:
Array
(
[0] => 0
[1] => 1
[2] => 8
[3] => 9
[4] => 16
[5] => 17
)
Its better to use substr_count . Check out on php.net
function getocurence($chaine,$rechercher)
{
$lastPos = 0;
$positions = array();
while (($lastPos = strpos($chaine, $rechercher, $lastPos))!== false)
{
$positions[] = $lastPos;
$lastPos = $lastPos + strlen($rechercher);
}
return $positions;
}
This can be done using strpos() function. The following code is implemented using for loop. This code is quite simple and pretty straight forward.
<?php
$str_test = "Hello World! welcome to php";
$count = 0;
$find = "o";
$positions = array();
for($i = 0; $i<strlen($str_test); $i++)
{
$pos = strpos($str_test, $find, $count);
if($pos == $count){
$positions[] = $pos;
}
$count++;
}
foreach ($positions as $value) {
echo '<br/>' . $value . "<br />";
}
?>
Use preg_match_all to find all occurrences.
preg_match_all('/(\$[a-z]+)/i', $str, $matches);
For further reference check this link.
Salman A has a good answer, but remember to make your code multibyte-safe. To get correct positions with UTF-8, use mb_strpos instead of strpos:
function strpos_all($haystack, $needle) {
$offset = 0;
$allpos = array();
while (($pos = mb_strpos($haystack, $needle, $offset)) !== FALSE) {
$offset = $pos + 1;
$allpos[] = $pos;
}
return $allpos;
}
print_r(strpos_all("aaa bbb aaa bbb aaa bbb", "aa"));
Another solution is to use explode():
public static function allSubStrPos($str, $del)
{
$searchArray = explode($del, $str);
unset($searchArray[count($searchArray) - 1]);
$positionsArray = [];
$index = 0;
foreach ($searchArray as $i => $s) {
array_push($positionsArray, strlen($s) + $index);
$index += strlen($s) + strlen($del);
}
return $positionsArray;
}
Simple strpos_all() function.
function strpos_all($haystack, $needle_regex)
{
preg_match_all('/' . $needle_regex . '/', $haystack, $matches, PREG_OFFSET_CAPTURE);
return array_map(function ($v) {
return $v[1];
}, $matches[0]);
}
Usage:
Simple string as needle.
$html = "dddasdfdddasdffff";
$needle = "asdf";
$all_positions = strpos_all($html, $needle);
var_dump($all_positions);
Output:
array(2) {
[0]=>
int(3)
[1]=>
int(10)
}
Or with regex as needle.
$html = "dddasdfdddasdffff";
$needle = "[d]{3}";
$all_positions = strpos_all($html, $needle);
var_dump($all_positions);
Output:
array(2) {
[0]=>
int(0)
[1]=>
int(7)
}
<?php
$mainString = "dddjmnpfdddjmnpffff";
$needle = "jmnp";
$lastPos = 0;
$positions = array();
while (($lastPos = strpos($html, $needle, $lastPos))!== false) {
$positions[] = $lastPos;
$lastPos = $lastPos + strlen($needle);
}
// Displays 3 and 10
foreach ($positions as $value) {
echo $value ."<br />";
}
?>
I have an uri and want to get an array of parts starting with full uri descent until I have only the first uri segment as part. I also have domain but the segments get stitched starting from the end back.
String 'a/b/c' to array('a/b/c', 'a/b', 'a')
String 'test.domain.com' to array('test.domain.com', 'domain.com', 'com')
Based on my initial method and the second method from the answers and a third method I like to know which one is the quickest and maybe there is a fourth method even better...
Method 1:
function explode_special($delimiter, $string, $reverse = false)
{
if (strpos($string, $delimiter) === false) return array($string);
$segments = explode($delimiter, $string);
if ( ! $reverse)
{
array_walk($segments, function($v, $k, $d) use(&$segments, &$delimiter) {
$d[$k] = implode($delimiter, array_slice($segments, 0, (count($segments) - $k)));
}, &$parts);
}
else
{
array_walk($segments, function($v, $k, $d) use(&$segments, &$delimiter) {
$d[$k] = implode($delimiter, array_slice($segments, $k));
}, &$parts);
}
return $parts;
}
Method 2:
function explode_special($delimiter, $string, $reverse = false)
{
if (strpos($string, $delimiter) === false) return array($string);
$segments = explode($delimiter, $string);
$parts = array();
while ($segments)
{
$parts[] = implode($delimiter, $segments);
if ($reverse)
{
array_shift($segments);
}
else
{
array_pop($segments);
}
}
return $parts;
}
Method 3:
function explode_special($delimiter, $string, $reverse = false)
{
if (strpos($string, $delimiter) === false) return array($string);
$parts = array($string);
for ($i = substr_count($string, $delimiter); $i > 0; $i--)
{
$parts[] = $string = $reverse
// ? trim(strstr($string, $delimiter), $delimiter)
? substr($string, strpos($string, $delimiter) + 1)
: substr($string, 0, strrpos($string, $delimiter));
}
return $parts;
}
Output:
// explode_special('/', 'a/b/c') = array('a/b/c', 'a/b', 'c');
// explode_special('.', 'test.domain.com', true) =
// array('test.domain.com', 'domain.com', 'com');
function multisplit($delim,$str) {
$parts = explode($delim,$str);
$arr = Array();
while($parts) {
$arr[] = implode($delim,$parts);
array_pop($parts);
}
return $arr;
}
With that function, you can just call multisplit("/","a/b/c"); and it will return ['a/b/c','a/b','a']. Similarly, call multisplit(".","test.domain.com"); and get ['test.domain.com','test.domain','test'].
I came up with the following
<?php
header("Content-type: text/plain"); //For display purposes only
/**
* Returns an array of strings with diminishing elements of $string according to $delimiter
*
* #param string $string The string
* #param string $delimiter The delimiter
*
* #return string[] Array of strings
*/
function degrading_split($string, $delimiter) {
$exploded = explode($delimiter, $string); //Array version of the string
$result = array(); //Initialize result array
foreach ($exploded as $key => $value) { //Iterate the array
$result[] = implode( #Implode...
$delimiter, #Using the original delimiter
array_slice($exploded, $key) #The complete $exploded array starting from the current $key
);
}
return $result;
}
print_r(degrading_split("a/b/c/d", "/"));
What you think about this with regex
function splitString($string,$delim) {
$arr = array();
preg_match_all('~['.$delim.']~U',$string,$delimCounter);
$arr[] = $string;
for ($i = 0;$i<count($delimCounter[0]);$i++) {
$string = $arr[] = preg_replace('~^[^.]*['.$delim.']~',"",$string);
}
return $arr;
}
How can i define multiple needles and still perform the same actions below. Im trying to define extra keywords such as numbers, numerals, etc... as of now i have to create a duplicate if loop with the minor keyword change.
if (stripos($data, 'digits') !== false) {
$arr = explode('+', $data);
for ($i = 1; $i < count($arr); $i += 2) {
$arr[$i] = preg_replace('/\d/', '', $arr[$i]);
}
$data = implode('+', $arr);
}
Create a function that loops through an array?
function check_matches ($data, $array_of_needles)
{
foreach ($array_of_needles as $needle)
{
if (stripos($data, $needle)!==FALSE)
{
return true;
}
}
return false;
}
if (check_matches($data, $array_of_needles))
{
//do the rest of your stuff
}
--edit added semicolon
function strposa($haystack, $needles=array(), $offset=0) {
$chr = array();
foreach($needles as $needle) {
$res = strpos($haystack, $needle, $offset);
if ($res !== false) $chr[$needle] = $res;
}
if(empty($chr)) return false;
return min($chr);
}
Usage:
$array = array('1','2','3','etc');
if (strposa($data, $array)) {
$arr = explode('+', $data);
for ($i = 1; $i < count($arr); $i += 2) {
$arr[$i] = preg_replace('/\d/', '', $arr[$i]);
}
$data = implode('+', $arr);
} else {
echo 'false';
}
function taken from https://stackoverflow.com/a/9220624/1018682
Though the previous answers are correct, but I'd like to add all the possible combinations, like you can pass the needle as array or string or integer.
To do that, you can use the following snippet.
function strposAll($haystack, $needles){
if(!is_array($needle)) $needles = array($needles); // if the $needle is not an array, then put it in an array
foreach($needles as $needle)
if( strpos($haystack, $needle) !== False ) return true;
return false;
}
You can now use the second parameter as array or string or integer, whatever you want.
The test below returns false ($pos = 0) when $haystack = "my keyword" and $needle = "my keyword", presumably because my stripos test returns 0 since there is no empty space in the barn.
What do I need to change in the comparison to return true in this case?
function my_test($post){
if($post->post_title == "") return false;
$haystack = my_esc2($post->post_title);
$needle = trim(my_getKeyword($post));
$pos = stripos($haystack, $needle);
if ($pos !== false) return true;
//returns 0 when $needle and $haystack are the same exact phrase. but should return 1
}
function my_getKeyword($post)
{
$myKeyword = get_post_meta($post->ID, '_my_keyword', true);
if($myKeyword == "") $myKeyword = $post->post_title;
$myKeyword = my_esc($myKeyword);
return " ".$myKeyword;
}
function my_esc($str, $quotation='"') {
if ($quotation != '"' && $quotation != "'") return false;
return str_replace($quotation, $quotation.$quotation, $str);
}
function my_esc2($str, $quotation='"') {
if ($quotation != '"' && $quotation != "'") return false;
return str_replace($quotation, '', $str);
}
If both strings are the same, stripos is supposed to return 0 as 0 is the position in the string where the match if found.
However, you are using the !== operator, so that test should return true anyway (by the way, you can just use return ($pos !== false)).
Are you sure you are getting to that statement, can you echo both $haystack and $needle right before the return statement?
It seems to me that haystack and needle are not the same or needle is not found or ($post->post_title == "")...
I have a php string containing the serialization of a javascript object :
$string = '{fu:"bar",baz:["bat"]}';
The actual string is far more complicated, of course, but still well-formed javascript. This is not standard JSON, so json_decode fails. Do you know any php library that would parse this string and return a php associative array ?
This sounded like a fun challenge, so I coded up a tiny parser :D
class JsParserException extends Exception {}
function parse_jsobj($str, &$data) {
$str = trim($str);
if(strlen($str) < 1) return;
if($str{0} != '{') {
throw new JsParserException('The given string is not a JS object');
}
$str = substr($str, 1);
/* While we have data, and it's not the end of this dict (the comma is needed for nested dicts) */
while(strlen($str) && $str{0} != '}' && $str{0} != ',') {
/* find the key */
if($str{0} == "'" || $str{0} == '"') {
/* quoted key */
list($str, $key) = parse_jsdata($str, ':');
} else {
$match = null;
/* unquoted key */
if(!preg_match('/^\s*[a-zA-z_][a-zA-Z_\d]*\s*:/', $str, $match)) {
throw new JsParserException('Invalid key ("'.$str.'")');
}
$key = $match[0];
$str = substr($str, strlen($key));
$key = trim(substr($key, 0, -1)); /* discard the ':' */
}
list($str, $data[$key]) = parse_jsdata($str, '}');
}
"Finshed dict. Str: '$str'\n";
return substr($str, 1);
}
function comma_or_term_pos($str, $term) {
$cpos = strpos($str, ',');
$tpos = strpos($str, $term);
if($cpos === false && $tpos === false) {
throw new JsParserException('unterminated dict or array');
} else if($cpos === false) {
return $tpos;
} else if($tpos === false) {
return $cpos;
}
return min($tpos, $cpos);
}
function parse_jsdata($str, $term="}") {
$str = trim($str);
if(is_numeric($str{0}."0")) {
/* a number (int or float) */
$newpos = comma_or_term_pos($str, $term);
$num = trim(substr($str, 0, $newpos));
$str = substr($str, $newpos+1); /* discard num and comma */
if(!is_numeric($num)) {
throw new JsParserException('OOPSIE while parsing number: "'.$num.'"');
}
return array(trim($str), $num+0);
} else if($str{0} == '"' || $str{0} == "'") {
/* string */
$q = $str{0};
$offset = 1;
do {
$pos = strpos($str, $q, $offset);
$offset = $pos;
} while($str{$pos-1} == '\\'); /* find un-escaped quote */
$data = substr($str, 1, $pos-1);
$str = substr($str, $pos);
$pos = comma_or_term_pos($str, $term);
$str = substr($str, $pos+1);
return array(trim($str), $data);
} else if($str{0} == '{') {
/* dict */
$data = array();
$str = parse_jsobj($str, $data);
return array($str, $data);
} else if($str{0} == '[') {
/* array */
$arr = array();
$str = substr($str, 1);
while(strlen($str) && $str{0} != $term && $str{0} != ',') {
$val = null;
list($str, $val) = parse_jsdata($str, ']');
$arr[] = $val;
$str = trim($str);
}
$str = trim(substr($str, 1));
return array($str, $arr);
} else if(stripos($str, 'true') === 0) {
/* true */
$pos = comma_or_term_pos($str, $term);
$str = substr($str, $pos+1); /* discard terminator */
return array(trim($str), true);
} else if(stripos($str, 'false') === 0) {
/* false */
$pos = comma_or_term_pos($str, $term);
$str = substr($str, $pos+1); /* discard terminator */
return array(trim($str), false);
} else if(stripos($str, 'null') === 0) {
/* null */
$pos = comma_or_term_pos($str, $term);
$str = substr($str, $pos+1); /* discard terminator */
return array(trim($str), null);
} else if(strpos($str, 'undefined') === 0) {
/* null */
$pos = comma_or_term_pos($str, $term);
$str = substr($str, $pos+1); /* discard terminator */
return array(trim($str), null);
} else {
throw new JsParserException('Cannot figure out how to parse "'.$str.'" (term is '.$term.')');
}
}
Usage:
$data = '{fu:"bar",baz:["bat"]}';
$parsed = array();
parse_jsobj($data, $parsed);
var_export($parsed);
Gives:
array (
'fu' => 'bar',
'baz' =>
array (
0 => 'bat',
),
)
Tested with these strings:
'{fu:"bar",baz:["bat"]}',
'{rec:{rec:{rec:false}}}',
'{foo:[1,2,[3,4]]}',
'{fu:{fu:"bar"},bar:{fu:"bar"}}',
'{"quoted key":[1,2,3]}',
'{und:undefined,"baz":[1,2,"3"]}',
'{arr:["a","b"],"baz":"foo","gar":{"faz":false,t:"2"},f:false}',
Pear Services_JSON will parse that string (tested version 1.31). But given that that is a JSON parser and that this isn't valid JSON you have no guarantee that future versions will still work.
I found out that the Yii-framework's CJSON::decode() function handles Javascript objects as well.
If you're not using Yii, you should be able to just use the source code
thank luttkens
the CJON::decode() class of the Yii-framework works perfectly !
require_once ($_SERVER['DOCUMENT_ROOT']."/phplib/CJSON.php");
$json = new CJSON();
$data = $json->decode('{ url : "/jslib/maps/marker/marker_red.png", height : 34, width : 20, anchorIcon : [5,25.5], anchorText : [0,2], }', true);
print_r( $data );
result :
Array
(
[url] => /jslib/maps/marker/marker_red.png
[height] => 34
[width] => 20
[anchorIcon] => Array
(
[0] => 5
[1] => 25.5
)
[anchorText] => Array
(
[0] => 0
[1] => 2
)
)
What about that library?
http://timwhitlock.info/tag/jparser/
I haven't tried it yet.