Найдите первый символ, который отличается между двумя строками

Учитывая две строки равной длины, есть ли элегантный способ получить смещение первого другого персонажа?

Очевидным решением будет:

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

Но для такой простой задачи это выглядит не совсем правильно.

Вы можете использовать свойство nice побитового XOR ( ^ ) для достижения этого: в основном, когда вы xor две строки вместе, одинаковые символы становятся нулевыми байтами ( "\0" ). Итак, если мы xor две строки, нам просто нужно найти позицию первого непустого байта, используя strspn :

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

Вот и все. Итак, давайте посмотрим на пример:

 $string1 = 'foobarbaz'; $string2 = 'foobarbiz'; $pos = strspn($string1 ^ $string2, "\0"); printf( 'First difference at position %d: "%s" vs "%s"', $pos, $string1[$pos], $string2[$pos] ); 

Это будет выводить:

Первое различие в позиции 7: «a» против «i»

Так что это нужно делать. Это очень эффективно, поскольку он использует только функции C и требует только одну копию памяти строки.

Редактировать: решение MultiByte по тем же линиям:

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

Сначала разность на уровне байта найдена с использованием вышеуказанного метода, а затем смещение отображается на уровень символов. Это делается с помощью функции mb_strcut , которая в основном является substr но соблюдает границы многобайтовых символов.

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

Это не так элегантно, как первое решение, но оно по-прежнему однострочное (и если вы используете кодировку по умолчанию немного проще):

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

Если вы преобразуете строку в массив значений одного символа в один байт, вы можете использовать функции сравнения массива для сравнения строк.

Вы можете добиться аналогичного результата с помощью метода XOR со следующим.

 $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]'."; 

Изменить: многобайтовое решение

 $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"; 

Я хотел добавить это как комментарий к лучшему ответу, но у меня недостаточно очков.

 $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'; } 
 string strpbrk ( string $haystack , string $char_list ) 

strpbrk () ищет строку haystack для char_list.

Возвращаемое значение представляет собой подстроку $ haystack, которая начинается с первого совпадающего символа. Как функция API, она должна быть незаметной. Затем прокрутите один раз, ища нулевое значение смещения возвращаемой строки, чтобы получить смещение.