Gibt es bei zwei gleich langen Zeichenfolgen eine elegante Möglichkeit, den Versatz des ersten unterschiedlichen Zeichens zu ermitteln?
Die offensichtliche Lösung wäre:
for ($offset = 0; $offset < $length; ++$offset) {
if ($str1[$offset] !== $str2[$offset]) {
return $offset;
}
}
Aber das sieht für eine so einfache Aufgabe nicht ganz richtig aus.
O(n)
und Sie werden untersuchen , bis müssenn
Zeichen.Antworten:
Sie können eine nette Eigenschaft von bitweisem XOR (
^
) verwenden , um dies zu erreichen: Wenn Sie zwei Zeichenfolgen zusammen xorieren, werden die gleichen Zeichen zu Null-Bytes ("\0"
). Wenn wir also die beiden Zeichenfolgen xorieren, müssen wir nur die Position des ersten Nicht-Null-Bytes ermitteln, indem wirstrspn
:$position = strspn($string1 ^ $string2, "\0");
Das ist alles dazu. Schauen wir uns also ein Beispiel an:
$string1 = 'foobarbaz'; $string2 = 'foobarbiz'; $pos = strspn($string1 ^ $string2, "\0"); printf( 'First difference at position %d: "%s" vs "%s"', $pos, $string1[$pos], $string2[$pos] );
Das wird ausgegeben:
Das sollte es also tun. Es ist sehr effizient, da nur C-Funktionen verwendet werden und nur eine einzige Kopie des Speichers der Zeichenfolge erforderlich ist.
Bearbeiten: Eine MultiByte-Lösung entlang derselben Linie:
function getCharacterOffsetOfDifference($str1, $str2, $encoding = 'UTF-8') { return mb_strlen( mb_strcut( $str1, 0, strspn($str1 ^ $str2, "\0"), $encoding ), $encoding ); }
Zuerst wird der Unterschied auf Byte-Ebene mit der obigen Methode ermittelt und dann der Offset auf die Zeichenebene abgebildet. Dies geschieht mit der
mb_strcut
Funktion, die im Grunde genommensubstr
jedoch die Grenzen von Multibyte-Zeichen berücksichtigt.var_dump(getCharacterOffsetOfDifference('foo', 'foa')); // 2 var_dump(getCharacterOffsetOfDifference('©oo', 'foa')); // 0 var_dump(getCharacterOffsetOfDifference('f©o', 'fªa')); // 1
Es ist nicht so elegant wie die erste Lösung, aber es ist immer noch ein Einzeiler (und wenn Sie die Standardcodierung etwas einfacher verwenden):
return mb_strlen(mb_strcut($str1, 0, strspn($str1 ^ $str2, "\0")));
quelle
Wenn Sie eine Zeichenfolge in ein Array mit Einzelbyte-Ein-Byte-Werten konvertieren, können Sie die Zeichenfolgen mithilfe der Array-Vergleichsfunktionen vergleichen.
Sie können mit der folgenden Methode ein ähnliches Ergebnis wie mit der XOR-Methode erzielen.
$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]'.";
Bearbeiten: Multibyte-Lösung
$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";
quelle
$array = preg_split('((.))u', $string, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
preg_split
Tipp, fügte ihn der Antwort hinzu.Ich wollte dies als Kommentar zur besten Antwort hinzufügen, aber ich habe nicht genug Punkte.
$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'; }
quelle
string strpbrk ( string $haystack , string $char_list )
strpbrk () durchsucht den Heuhaufen-String nach einer char_list.
Der Rückgabewert ist die Teilzeichenfolge von $ haystack, die beim ersten übereinstimmenden Zeichen beginnt. Als API-Funktion sollte es flink sein. Durchlaufen Sie dann einmal und suchen Sie nach dem Offset Null der zurückgegebenen Zeichenfolge, um Ihren Offset zu erhalten.
quelle
abcdef
und char_list istfedcba
, wird die gesamte Zeichenfolge zurückgegeben (da siea
in der char-Liste enthalten ist). Während diese Funktion für eine sehr begrenzte Teilmenge möglicher Eingaben funktioniert, funktioniert sie nicht generisch, sodass sie keine gute Antwort auf die Frage ist.abcdef
wenn die char_list ebenfalls istabcdef
. Es ist also nur "zufällig", dass die richtige Antwort zurückgegeben wird.