Find first character that is different between two strings

php, string

Solution

You can use a nice property of bitwise XOR (`^`) to achieve this: Basically, when you xor two strings together, the characters that are the same will become null bytes (`"\0"`). So if we xor the two strings, we just need to find the position of the first non-null byte using `strspn`:

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

That's all there is to it. So let's look at an example:

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

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

That will output:

First difference at position 7: "a" vs "i"

So that should do it. It's very efficient since it's only using C functions, and requires only a single copy of memory of the string.

Edit: A MultiByte Solution Along The Same Lines:

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

First the difference at the byte level is found using the above method and then the offset is mapped to the character level. This is done using the `mb_strcut` function, which is basically `substr` but honoring multibyte character boundaries.

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

It's not as elegant as the first solution, but it's still a one-liner (and if you use the default encoding a little bit simpler):

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

Problem

Given two equal-length strings, is there an elegant way to get the offset of the first different character? The obvious solution would be: ``` for ($offset = 0; $offset < $length; ++$offset) { if ($str1[$offset] !== $str2[$offset]) { return $offset; } } ``` But that doesn't look quite right, for such a simple task.

Original source

Related problems