Найдите первый символ, который отличается между двумя строками
Учитывая две строки с равной длиной, есть ли элегантный способ получить смещение первого другого символа?
Очевидным решением было бы следующее:
for ($offset = 0; $offset < $length; ++$offset) {
if ($str1[$offset] !== $str2[$offset]) {
return $offset;
}
}
Но это не выглядит совершенно правильно, для такой простой задачи.
Ответы
Ответ 1
Вы можете использовать приятное свойство побитового 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" vs "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")));
Ответ 2
Если вы преобразуете строку в массив из одного символа в один байт, вы можете использовать функции сравнения массива для сравнения строк.
Вы можете добиться аналогичного результата с помощью метода 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";
Ответ 3
Я хотел добавить это как комментарий к лучшему ответу, но у меня недостаточно очков.
$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';
}
Ответ 4
string strpbrk ( string $haystack , string $char_list )
strpbrk() выполняет поиск строки haystack для char_list.
Возвращаемое значение представляет собой подстроку $haystack, которая начинается с первого совпадающего символа.
Как функция API, она должна быть незаметной. Затем прокрутите один раз, ища нулевое значение смещения возвращаемой строки, чтобы получить смещение.