68 votes

Trouver premier caractère différent entre deux chaînes de caractères

Compte tenu de l'égalité des deux chaînes de longueur, est-il un moyen élégant pour obtenir le décalage de la première de caractères différents?

La solution évidente serait:

for ($offset = 0; $offset < $length; ++$offset) {
    if ($str1[$offset] !== $str2[$offset]) {
        return $offset;
    }
}

Mais qui n'a pas l'air tout à fait raison, pour une tâche simple.

172voto

ircmaxell Points 74865

Vous pouvez utiliser une belle propriété de XOR au niveau du bit (^) pour atteindre ce: en principe, lorsque vous xor de deux cordes en même temps, les caractères sont les mêmes deviendront des octets nuls ("\0"). Donc, si nous xor, les deux chaînes, nous avons juste besoin de trouver la position du premier non-octet null à l'aide de strspn:

$position = strspn($string1 ^ $string2, "\0");

C'est tout là est à lui. Alors, regardons un exemple:

$string1 = 'foobarbaz';
$string2 = 'foobarbiz';
$pos = strspn($string1 ^ $string2, "\0");

printf(
    'First difference at position %d: "%s" vs "%s"',
    $pos, $string1[$pos], $string2[$pos]
);

Qui sera de sortie:

Première différence à la position 7: "un" vs "je"

Donc ça devrait le faire. C'est très efficace car elle n'utilise que les fonctions C, et ne nécessite qu'une seule copie de la mémoire de la chaîne.

Edit: Une Solution Multi-Octets Dans Le Même Sens:

function getCharacterOffsetOfDifference($str1, $str2, $encoding = 'UTF-8') {
    return mb_strlen(
        mb_strcut(
            $str1,
            0, strspn($str1 ^ $str2, "\0"),
            $encoding
        ),
        $encoding
    );
}

D'abord la différence au niveau de l'octet est trouvé en utilisant la méthode ci-dessus, et le décalage est mappé au niveau du caractère. Ceci est fait en utilisant l' mb_strcut de la fonction, qui est fondamentalement substr mais en honorant les jeux de caractères multioctets limites.

var_dump(getCharacterOffsetOfDifference('foo', 'foa')); // 2
var_dump(getCharacterOffsetOfDifference('©oo', 'foa')); // 0
var_dump(getCharacterOffsetOfDifference('f©o', 'fªa')); // 1

Ce n'est pas aussi élégante que la première solution, mais il est encore un one-liner (et si vous utilisez le codage par défaut un peu plus simple):

return mb_strlen(mb_strcut($str1, 0, strspn($str1 ^ $str2, "\0")));

16voto

Steve Buzonas Points 1332

Si vous convertir une chaîne en un tableau de caractère unique, un octet de valeurs, vous pouvez utiliser le tableau des fonctions de comparaison pour comparer des chaînes de caractères.

Vous pouvez obtenir un résultat similaire à la XOR méthode avec le suivant.

$string1 = 'foobarbaz';
$string2 = 'foobarbiz';

$array1 = str_split($string1);
$array2 = str_split($string2);

$result = array_diff_assoc($array1, $array2);

$num_diff = count($result);
$first_diff = key($result);

echo "There are " . $num_diff . " differences between the two strings. <br />";
echo "The first difference between the strings is at position " . $first_diff . ". (Zero Index) '$string1[$first_diff]' vs '$string2[$first_diff]'.";

Edit: Solution Multi-Octets

$string1 = 'foorbarbaz';
$string2 = 'foobarbiz';

$array1 = preg_split('((.))u', $string1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
$array2 = preg_split('((.))u', $string2, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);

$result = array_diff_assoc($array1, $array2);

$num_diff = count($result);
$first_diff = key($result);

echo "There are " . $num_diff . " differences between the two strings.\n";
echo "The first difference between the strings is at position " . $first_diff . ". (Zero Index) '$string1[$first_diff]' vs '$string2[$first_diff]'.\n";

4voto

Bradley Slavik Points 154

Je voulais l'ajouter comme commentaire à la meilleure réponse, mais je n'ai pas assez de points.

$string1 = 'foobarbaz';
$string2 = 'foobarbiz';
$pos = strspn($string1 ^ $string2, "\0");

if ($pos < min(strlen($string1), strlen($string2)){
    printf(
        'First difference at position %d: "%s" vs "%s"',
        $pos, $string1[$pos], $string2[$pos]
    );
} else if ($pos < strlen($string1)) {
    print 'String1 continues with' . substr($string1, $pos);
} else if ($pos < strlen($string2)) {
    print 'String2 continues with' . substr($string2, $pos);
} else {
    print 'String1 and String2 are equal';
}

-5voto

Sinthia V Points 1169
string strpbrk ( string $haystack , string $char_list )

strpbrk() recherche la botte de foin de chaîne pour une char_list.

La valeur de retour est la sous-chaîne de $haystack, qui commence à la première character. Comme une fonction de l'API, il devrait être zippy. Ensuite une boucle à travers une fois, à la recherche de décalage de zéro de la chaîne de retour pour obtenir votre décalage.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X