2010-11-25 37 views
8

Je suis à la recherche d'un moyen simple de trouver correspondant à des parties de deux chaînes en PHP (en particulier dans le contexte d'un URI)correspondant à des parties de deux chaînes en PHP

Par exemple, considérons les deux chaînes:

http://2.2.2.2/~machinehost/deployment_folder/

et

/~ machinehost/deployment_folder/utilisateurs/bob/paramètres

Ce que je besoin est de couper la partie d'adaptation de ces deux chaînes à partir de la deuxième chaîne de caractères, résultant en:

utilisateurs/bob/paramètres

avant d'ajouter la première chaîne de caractères en tant que préfixe, formant un URI absolu.

Existe-t-il un moyen simple (en PHP) de comparer deux chaînes arbitraires pour les sous-chaînes correspondantes en leur sein?

EDIT: comme l'a souligné, je voulais dire la mise en correspondance le plus long sous-chaîne commune aux deux chaînes

+1

Quels sont les critères ici? Car techniquement, le h dans "http" correspondra à h dans "machinehost". Vous allez devoir être beaucoup plus spécifique que "les sous-chaînes correspondantes". – cdhowie

+0

Désolé, vous avez tout à fait raison. Je voulais dire correspondre à la sous-chaîne la plus longue possible. – ubermensch

Répondre

3

This serait la réponse. Fonction PHP prête à l'emploi.

+0

sympa, merci pour ça! – ubermensch

+1

Ceci est une [réponse de lien seulement] (http://meta.stackexchange.com/questions/65277/are-link-only-answers-poor-practice). Les liens sont excellents, mais ils [ne devraient pas être la seule partie d'une réponse] (http://meta.stackexchange.com/questions/8231/are-answers-that-just-contain-links-elsewhere-really-good -réponses). – Jimbo

0

Je ne suis pas sûr de comprendre votre demande complète, mais l'idée est:

Soit A votre URL et B est votre "/ ~ machinehost/deployment_folder/utilisateurs/bob/paramètres"

  • recherche B en A -> vous obtenez un indice i (où i est la position de la première/B en A)
  • let l = longueur (A)
  • Vous devez couper B de (li) à la longueur (B) de saisir la dernière partie de B (/ Users/bob/paramètres)

Je ne l'ai pas testé Pourtant, si vous en avez vraiment besoin, je peux vous aider à faire fonctionner cette solution brillante (ironique).

Notez qu'il peut être possible avec des expressions régulières comme

$pattern = "$B(.*?)" 
$res = array(); 
preg_match_all($pattern, $A, $res); 

Edit: Je pense que votre dernier commentaire invalident ma réponse. Mais ce que vous voulez, c'est trouver des sous-chaînes. Donc, vous pouvez commencer par un algorithme lourd en essayant de trouver B [1: i] dans A pour i dans {2, longueur (B)} et ensuite utiliser quelques trucs dynamic programming.

2

En supposant que votre chaînes sont $a et $b, respectivement, vous pouvez utiliser ceci:

$a = 'http://2.2.2.2/~machinehost/deployment_folder/'; 
$b = '/~machinehost/deployment_folder/users/bob/settings'; 

$len_a = strlen($a); 
$len_b = strlen($b); 

for ($p = max(0, $len_a - $len_b); $p < $len_b; $p++) 
    if (substr($a, $len_a - ($len_b - $p)) == substr($b, 0, $len_b - $p)) 
     break; 

$result = $a.substr($b, $len_b - $p); 

echo $result; 

Ce résultat est http://2.2.2.2/~machinehost/deployment_folder/users/bob/settings.

0

il ne semble pas être un hors du code de la boîte là-bas pour vos besoins. Alors regardons pour un moyen simple.Pour cet exercice, j'ai utilisé deux méthodes, une pour trouver la plus longue correspondance, et une autre pour couper la partie correspondante.

Le FindLongestMatch() méthode, prend part un chemin, pièce par pièce cherche une correspondance dans l'autre chemin, en gardant juste une partie, la plus longue (pas de tableaux, pas de tri). La méthode RemoveLongestMatch() prend le suffixe ou «reste» après la position trouvée la plus longue.

Voici le code source complet:

<?php 

function FindLongestMatch($relativePath, $absolutePath) 
{ 
    static $_separator = '/'; 
    $splitted = array_reverse(explode($_separator, $absolutePath)); 

    foreach ($splitted as &$value) 
    { 
     $matchTest = $value.$_separator.$match; 
     if(IsSubstring($relativePath, $matchTest)) 
      $match = $matchTest; 

     if (!empty($value) && IsNewMatchLonger($match, $longestMatch)) 
      $longestMatch = $match; 
    } 

    return $longestMatch; 
} 

//Removes from the first string the longest match. 
function RemoveLongestMatch($relativePath, $absolutePath) 
{ 
    $match = findLongestMatch($relativePath, $absolutePath); 
    $positionFound = strpos($relativePath, $match);  
    $suffix = substr($relativePath, $positionFound + strlen($match)); 

    return $suffix; 
} 

function IsNewMatchLonger($match, $longestMatch) 
{ 
    return strlen($match) > strlen($longestMatch); 
} 

function IsSubstring($string, $subString) 
{ 
    return strpos($string, $subString) > 0; 
} 

Ceci est un sous-ensemble représentatif de cas de test:

//TEST CASES 
echo "<br>-----------------------------------------------------------"; 
echo "<br>".$absolutePath = 'http://2.2.2.2/~machinehost/deployment_folder/'; 
echo "<br>".$relativePath = '/~machinehost/deployment_folder/users/bob/settings'; 
echo "<br>Longest match: ".findLongestMatch($relativePath, $absolutePath); 
echo "<br>Suffix: ".removeLongestMatch($relativePath, $absolutePath); 

echo "<br>-----------------------------------------------------------"; 
echo "<br>".$absolutePath = 'http://1.1.1.1/root/~machinehost/deployment_folder/'; 
echo "<br>".$relativePath = '/root/~machinehost/deployment_folder/users/bob/settings'; 
echo "<br>Longest match: ".findLongestMatch($relativePath, $absolutePath); 
echo "<br>Suffix: ".removeLongestMatch($relativePath, $absolutePath); 

echo "<br>-----------------------------------------------------------"; 
echo "<br>".$absolutePath = 'http://2.2.2.2/~machinehost/deployment_folder/users/'; 
echo "<br>".$relativePath = '/~machinehost/deployment_folder/users/bob/settings'; 
echo "<br>Longest match: ".findLongestMatch($relativePath, $absolutePath); 
echo "<br>Suffix: ".removeLongestMatch($relativePath, $absolutePath); 

echo "<br>-----------------------------------------------------------"; 
echo "<br>".$absolutePath = 'http://3.3.3.3/~machinehost/~machinehost/subDirectory/deployment_folder/'; 
echo "<br>".$relativePath = '/~machinehost/subDirectory/deployment_folderX/users/bob/settings'; 
echo "<br>Longest match: ".findLongestMatch($relativePath, $absolutePath); 
echo "<br>Suffix: ".removeLongestMatch($relativePath, $absolutePath); 

exécution de cas de test précédent fournit la sortie suivante:

http://2.2.2.2/~machinehost/deployment_folder/ 
/~machinehost/deployment_folder/users/bob/settings 
Longuest match: ~machinehost/deployment_folder/ 
Suffix: users/bob/settings 

http://1.1.1.1/root/~machinehost/deployment_folder/ 
/root/~machinehost/deployment_folder/users/bob/settings 
Longuest match: root/~machinehost/deployment_folder/ 
Suffix: users/bob/settings 

http://2.2.2.2/~machinehost/deployment_folder/users/ 
/~machinehost/deployment_folder/users/bob/settings 
Longuest match: ~machinehost/deployment_folder/users/ 
Suffix: bob/settings 

http://3.3.3.3/~machinehost/~machinehost/subDirectory/deployment_folder/ 
/~machinehost/subDirectory/deployment_folderX/users/bob/settings 
Longuest match: ~machinehost/subDirectory/ 
Suffix: deployment_folderX/users/bob/settings 

Peut-être vous pouvez prendre l'idée de ce morceau de code et le transformer en quelque chose que vous trouvez utile f ou votre projet actuel. Dites-moi si cela a fonctionné pour vous aussi. Au fait, M. oreX répond bien aussi.

0

La recherche de la correspondance commune la plus longue peut également être effectuée à l'aide de regex.

La fonction ci-dessous prendra deux chaînes, utilisez-en une pour créer une regex et l'exécuter par rapport à l'autre.

/** 
* Determine the longest common match within two strings 
* 
* @param string $str1 
* @param string $str2 Two strings in any order. 
* @param boolean $case_sensitive Set to true to force 
* case sensitivity. Default: false (case insensitive). 
* @return string The longest string - first match. 
*/ 
function get_longest_common_subsequence($str1, $str2, $case_sensitive = false) { 
    // We'll use '#' as our regex delimiter. Any character can be used as we'll quote the string anyway, 
    $delimiter = '#'; 

    // We'll find the shortest string and use that to create our regex. 
    $l1 = strlen($str1); 
    $l2 = strlen($str2); 
    $str = $l1 <= $l2 ? $str1 : $str2; 
    $l = min($l1, $l2); 

    // Regex for each character will be of the format (?:a(?=b))? 
    // We also need to capture the last character, but this prevents us from matching strings with a single character. (?:.|c)? 
    $reg = $delimiter; 
    for ($i = 0; $i < $l; $i++) { 
     $a = preg_quote($str[ $i ], $delimiter); 
     $b = $i + 1 < $l ? preg_quote($str[ $i + 1 ], $delimiter) : false; 
     $reg .= sprintf($b !== false ? '(?:%s(?=%s))?' : '(?:.|%s)?', $a, $b); 
    } 
    $reg .= $delimiter; 
    if (! $case_sensitive) { 
     $reg .= 'i'; 
    } 
    // Resulting example regex from a string 'abbc': 
    // '#(?:a(?=b))?(?:b(?=b))?(?:b(?=c))?(?:.|c)?#i'; 

    // Perform our regex on the remaining string 
    $str = $l1 <= $l2 ? $str2 : $str1; 
    if (preg_match_all($reg, $str, $matches)) { 
     // $matches is an array with a single array with all the matches. 
     return array_reduce($matches[0], function($a, $b) { 
      $al = strlen($a); 
      $bl = strlen($b); 
      // Return the longest string, as long as it's not a single character. 
      return $al >= $bl || $bl <= 1 ? $a : $b; 
     }, ''); 
    } 

    // No match - Return an empty string. 
    return ''; 
} 

Il générerons une expression régulière en utilisant la plus courte des deux chaînes, bien que la performance sera très probablement le même de toute façon. Il peut incorrectement faire correspondre des chaînes avec des sous-chaînes récurrentes, et nous sommes limités à des chaînes de caractères de deux caractères ou plus. Par exemple:

// Works as intended. 
get_longest_common_subsequence('abbc', 'abc') === 'ab'; 

// Returns incorrect substring based on string length and recurring substrings. 
get_longest_common_subsequence('abbc', 'abcdef') === 'abc'; 

// Does not return any matches. 
get_longest_common_subsequence('abc', 'ace') === ''; 

Quoiqu'il en soit, il fonctionne en utilisant une autre méthode et l'expression rationnelle peut être affinée pour faire face aux situations supplémentaires.