Wenn Sie Strcmp () verwenden, um lateinische Buchstaben mit Chinesen, Emoticons oder Tönen in PHP zu vergleichen, werden Sie Phänomene wie "Das Vergleichsergebnis ist falsch", "die Anzeige ist ein Fragezeichen/ein kleines Quadrat" und "seltsames Sortieren". Viele Leute nennen diesen "verstümmelten Code" zusammen. Tatsächlich hat Strcmp () selbst nicht die Fähigkeit, "Charaktere durcheinander zu bringen" - es vergleicht nur die "rohe binäre" von zwei Saiten. Die Probleme liegen oft in inkonsistenter Charaktercodierung , unterschiedlicher Textnormalisierung oder im falschen Vergleichsinstrument .
Im Folgenden werden die wichtigsten Gründe und operativen Lösungen gleichzeitig erläutert.
Binärer sicherer, Fallempfindlichkeit : STRCMP ($ A, $ B) vergleicht $ A und $ B in Byte -Bestellung und Rückgabe <0/0 /> 0 . Es versteht weder UTF-8, GBK, Emoji noch die "alphabetischen Sortierregeln", und es wird auch keine Fallfaltung oder Akzentverarbeitung durchgeführt.
Schlussfolgerung : Wenn zwei Zeichenfolgen unterschiedliche Codierungen verwenden oder beide UTF-8 sind, jedoch unterschiedliche Byt-Sequenzen (z. B. BOM, unterschiedliche normalisierte Formen) aufweisen, sieht das Ergebnis von STRCMP () "unangemessen aus" aus.
<span><span><span class="hljs-meta"><?php</span></span><span>
</span><span><span class="hljs-title function_ invoke__">var_dump</span></span><span>(</span><span><span class="hljs-title function_ invoke__">strcmp</span></span><span>(</span><span><span class="hljs-string">"a"</span></span><span>, </span><span><span class="hljs-string">"b"</span></span><span>)); </span><span><span class="hljs-comment">// int(-1) Normal:a < b</span></span><span>
</span><span><span class="hljs-title function_ invoke__">var_dump</span></span><span>(</span><span><span class="hljs-title function_ invoke__">strcmp</span></span><span>(</span><span><span class="hljs-string">"chinesisch"</span></span><span>, </span><span><span class="hljs-string">"Charakter"</span></span><span>)); </span><span><span class="hljs-comment">// Verhältnismäßig UTF-8 的Charakter节序,Das Ergebnis ist möglicherweise nicht konsistent“chinesischCharakter拼音顺序”</span></span><span>
</span></span> Inkonsistente Codierung (UTF-8 gegen GBK usw.)
Das gleiche "Chinesisch" ist UTF-8 und der andere ist GBK und die Bytes sind völlig unterschiedlich. STRCMP () befasst sich nur mit Bytes, natürlich "verlegt".
UTF-8 BOM und unsichtbare Zeichen <br> Das Einfügen von BOM (EF BB BF) , Null-Width Space (ZWSP) und unsichtbare Steuerzeichen in den Dateiheader oder die Eingabe macht das erste Byte/Last-Byte anders, was zu einer "empörenden".
Normalisierte Unterschiede (NFC/NFD)
é kann ein einzelnes Zeichen (NFC) oder ein "E + kombinierter Akzent" (NFD) sein. Die menschlichen Augen sind gleich, die Bytes sind unterschiedlich, Strcmp () wird als ungleich oder abnormal sortiert.
Erwarten Sie "menschliche Sortier-/Sprachregeln", verwenden Sie jedoch Byte -Vergleich <br> Willst du nach chinesischem Pinyin, Deutsch?, Französische Akzente, japanische Kanna regiert? STRCMP () versteht diese nicht und benötigt ein regionales Vergleichsinstrument .
Eingabe Unified : Datenbankverbindung, HTTP-Header, Vorlagendatei, CLI-Umgebung, der vollständige Link ist auf UTF-8 gesetzt.
Entfernen Sie die BOM/Steuerzeichen :
<span><span><span class="hljs-meta"><?php</span></span><span>
</span><span><span class="hljs-function"><span class="hljs-keyword">function</span></span></span><span> </span><span><span class="hljs-title">strip_bom_and_controls</span></span><span>(</span><span><span class="hljs-params"><span class="hljs-keyword">string</span></span></span><span> </span><span><span class="hljs-variable">$s</span></span><span>): </span><span><span class="hljs-title">string</span></span><span> {
</span><span><span class="hljs-comment">// gehen BOM</span></span><span>
</span><span><span class="hljs-variable">$s</span></span><span> = </span><span><span class="hljs-title function_ invoke__">preg_replace</span></span><span>(</span><span><span class="hljs-string">'/^\xEF\xBB\xBF/'</span></span><span>, </span><span><span class="hljs-string">''</span></span><span>, </span><span><span class="hljs-variable">$s</span></span><span>);
</span><span><span class="hljs-comment">// gehen常见零宽Charakter符:ZWSP, ZWNJ, ZWJ, NBSP …</span></span><span>
</span><span><span class="hljs-variable">$s</span></span><span> = </span><span><span class="hljs-title function_ invoke__">preg_replace</span></span><span>(</span><span><span class="hljs-string">'/[\x{200B}\x{200C}\x{200D}\x{00A0}]/u'</span></span><span>, </span><span><span class="hljs-string">''</span></span><span>, </span><span><span class="hljs-variable">$s</span></span><span>);
</span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-variable">$s</span></span><span>;
}
</span></span>Bei Bedarf konvertieren :
<span><span><span class="hljs-meta"><?php</span></span><span>
</span><span><span class="hljs-variable">$clean</span></span><span> = </span><span><span class="hljs-title function_ invoke__">mb_convert_encoding</span></span><span>(</span><span><span class="hljs-variable">$input</span></span><span>, </span><span><span class="hljs-string">'UTF-8'</span></span><span>, </span><span><span class="hljs-string">'UTF-8,GBK,GB2312,BIG5,ISO-8859-1'</span></span><span>);
</span></span>Installieren/Aktivieren Sie die INTL -Erweiterung und verwenden Sie Normalizer , um sie mit NFC zu vereinen:
<span><span><span class="hljs-meta"><?php</span></span><span>
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-title function_ invoke__">class_exists</span></span><span>(</span><span><span class="hljs-string">'Normalizer'</span></span><span>)) {
</span><span><span class="hljs-variable">$a</span></span><span> = </span><span><span class="hljs-title class_">Normalizer</span></span><span>::</span><span><span class="hljs-title function_ invoke__">normalize</span></span><span>(</span><span><span class="hljs-variable">$a</span></span><span>, </span><span><span class="hljs-title class_">Normalizer</span></span><span>::</span><span><span class="hljs-variable constant_">FORM_C</span></span><span>);
</span><span><span class="hljs-variable">$b</span></span><span> = </span><span><span class="hljs-title class_">Normalizer</span></span><span>::</span><span><span class="hljs-title function_ invoke__">normalize</span></span><span>(</span><span><span class="hljs-variable">$b</span></span><span>, </span><span><span class="hljs-title class_">Normalizer</span></span><span>::</span><span><span class="hljs-variable constant_">FORM_C</span></span><span>);
}
</span></span>Immer noch "Byte -Konsistenz" : Verwenden Sie Strcmp () ; oder einen Fall-unempfindlichen Byte-Vergleich verwenden, verwenden Sie StrCasecmp () (gleich nach Byte, ASCII-Regeln).
Vergleich von Regeln für menschliche Lesbare/Sprach (Sortieren/Deduplizierung/Befund) : Verwenden Sie den Intl \ Collator (regionaler Vergleich, Akzent, Großbuchstaben, Großbuchstaben und Variante).
<span><span><span class="hljs-meta"><?php</span></span><span>
</span><span><span class="hljs-variable">$coll</span></span><span> = </span><span><span class="hljs-keyword">new</span></span><span> </span><span><span class="hljs-title class_">\Collator</span></span><span>(</span><span><span class="hljs-string">'zh_CN'</span></span><span>); </span><span><span class="hljs-comment">// oder 'zh-Hans-CN', 'en_US', 'de_DE' Warten</span></span><span>
</span><span><span class="hljs-variable">$coll</span></span><span>-></span><span><span class="hljs-title function_ invoke__">setStrength</span></span><span>(</span><span><span class="hljs-title class_">\Collator</span></span><span>::</span><span><span class="hljs-variable constant_">SECONDARY</span></span><span>); </span><span><span class="hljs-comment">// 忽略大小写但区分重音Warten</span></span><span>
</span><span><span class="hljs-title function_ invoke__">var_dump</span></span><span>(</span><span><span class="hljs-variable">$coll</span></span><span>-></span><span><span class="hljs-title function_ invoke__">compare</span></span><span>(</span><span><span class="hljs-string">'chinesisch'</span></span><span>, </span><span><span class="hljs-string">'Charakter'</span></span><span>)); </span><span><span class="hljs-comment">// -1/0/1,Basierend auf sprachlichen Regeln</span></span><span>
</span></span>Müssen "Anzahl der Zeichen/verkürzt/durchquert auf Benutzer visuell" : Verwenden Sie GraphEm_* -Serie (gleich wie intl ), um "Gramem -Cluster" zu verarbeiten, um zu vermeiden, dass ein Emoji in die Hälfte geschnitten wird:
<span><span><span class="hljs-meta"><?php</span></span><span>
</span><span><span class="hljs-variable">$text</span></span><span> = </span><span><span class="hljs-string">"?????Entwicklung"</span></span><span>; </span><span><span class="hljs-comment">// Enthalten ZWJ Stecker emoji</span></span><span>
</span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-title function_ invoke__">grapheme_substr</span></span><span>(</span><span><span class="hljs-variable">$text</span></span><span>, </span><span><span class="hljs-number">0</span></span><span>, </span><span><span class="hljs-number">2</span></span><span>); </span><span><span class="hljs-comment">// ?????offen</span></span><span>
</span></span>Multi-Byte-Vergleiche, die ignoriert werden müssen : Verwenden Sie MB_STRTOLOWER oder MB_CONVERT_CASE, bevor Sie vergleichen (denken Sie daran, zuerst zu normalisieren):
<span><span><span class="hljs-meta"><?php</span></span><span>
</span><span><span class="hljs-variable">$a</span></span><span> = </span><span><span class="hljs-title class_">Normalizer</span></span><span>::</span><span><span class="hljs-title function_ invoke__">normalize</span></span><span>(</span><span><span class="hljs-variable">$a</span></span><span>, </span><span><span class="hljs-title class_">Normalizer</span></span><span>::</span><span><span class="hljs-variable constant_">FORM_C</span></span><span>);
</span><span><span class="hljs-variable">$b</span></span><span> = </span><span><span class="hljs-title class_">Normalizer</span></span><span>::</span><span><span class="hljs-title function_ invoke__">normalize</span></span><span>(</span><span><span class="hljs-variable">$b</span></span><span>, </span><span><span class="hljs-title class_">Normalizer</span></span><span>::</span><span><span class="hljs-variable constant_">FORM_C</span></span><span>);
</span><span><span class="hljs-variable">$aFold</span></span><span> = </span><span><span class="hljs-title function_ invoke__">mb_strtolower</span></span><span>(</span><span><span class="hljs-variable">$a</span></span><span>, </span><span><span class="hljs-string">'UTF-8'</span></span><span>);
</span><span><span class="hljs-variable">$bFold</span></span><span> = </span><span><span class="hljs-title function_ invoke__">mb_strtolower</span></span><span>(</span><span><span class="hljs-variable">$b</span></span><span>, </span><span><span class="hljs-string">'UTF-8'</span></span><span>);
</span><span><span class="hljs-title function_ invoke__">var_dump</span></span><span>(</span><span><span class="hljs-title function_ invoke__">strcmp</span></span><span>(</span><span><span class="hljs-variable">$aFold</span></span><span>, </span><span><span class="hljs-variable">$bFold</span></span><span>) === </span><span><span class="hljs-number">0</span></span><span>);
</span></span> <span><span><span class="hljs-meta"><?php</span></span><span>
</span><span><span class="hljs-variable">$names</span></span><span> = [</span><span><span class="hljs-string">'Zhang San'</span></span><span>, </span><span><span class="hljs-string">'Li Si'</span></span><span>, </span><span><span class="hljs-string">'Wang Wu'</span></span><span>, </span><span><span class="hljs-string">'Ali'</span></span><span>, </span><span><span class="hljs-string">'Cao Cao'</span></span><span>];
</span><span><span class="hljs-variable">$coll</span></span><span> = </span><span><span class="hljs-keyword">new</span></span><span> </span><span><span class="hljs-title class_">\Collator</span></span><span>(</span><span><span class="hljs-string">'zh_CN@collation=pinyin'</span></span><span>); </span><span><span class="hljs-comment">// System erfordern ICU Unterstützung</span></span><span>
</span><span><span class="hljs-variable">$coll</span></span><span>-></span><span><span class="hljs-title function_ invoke__">sort</span></span><span>(</span><span><span class="hljs-variable">$names</span></span><span>);
</span><span><span class="hljs-title function_ invoke__">print_r</span></span><span>(</span><span><span class="hljs-variable">$names</span></span><span>);
</span></span> <span><span><span class="hljs-meta"><?php</span></span><span>
</span><span><span class="hljs-variable">$input</span></span><span> = [</span><span><span class="hljs-string">'café'</span></span><span>, </span><span><span class="hljs-string">'Cafe'</span></span><span>, </span><span><span class="hljs-string">'CAFé'</span></span><span>, </span><span><span class="hljs-string">'cafe'</span></span><span>];
</span><span><span class="hljs-variable">$coll</span></span><span> = </span><span><span class="hljs-keyword">new</span></span><span> </span><span><span class="hljs-title class_">\Collator</span></span><span>(</span><span><span class="hljs-string">'fr_FR'</span></span><span>);
</span><span><span class="hljs-variable">$coll</span></span><span>-></span><span><span class="hljs-title function_ invoke__">setStrength</span></span><span>(</span><span><span class="hljs-title class_">\Collator</span></span><span>::</span><span><span class="hljs-variable constant_">PRIMARY</span></span><span>); </span><span><span class="hljs-comment">// Ignorieren Sie Akzente und Großbuchstaben</span></span><span>
</span><span><span class="hljs-variable">$unique</span></span><span> = [];
</span><span><span class="hljs-keyword">foreach</span></span><span> (</span><span><span class="hljs-variable">$input</span></span><span> </span><span><span class="hljs-keyword">as</span></span><span> </span><span><span class="hljs-variable">$s</span></span><span>) {
</span><span><span class="hljs-variable">$sN</span></span><span> = </span><span><span class="hljs-title class_">Normalizer</span></span><span>::</span><span><span class="hljs-title function_ invoke__">normalize</span></span><span>(</span><span><span class="hljs-variable">$s</span></span><span>, </span><span><span class="hljs-title class_">Normalizer</span></span><span>::</span><span><span class="hljs-variable constant_">FORM_C</span></span><span>);
</span><span><span class="hljs-variable">$found</span></span><span> = </span><span><span class="hljs-literal">false</span></span><span>;
</span><span><span class="hljs-keyword">foreach</span></span><span> (</span><span><span class="hljs-variable">$unique</span></span><span> </span><span><span class="hljs-keyword">as</span></span><span> </span><span><span class="hljs-variable">$u</span></span><span>) {
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$coll</span></span><span>-></span><span><span class="hljs-title function_ invoke__">compare</span></span><span>(</span><span><span class="hljs-variable">$sN</span></span><span>, </span><span><span class="hljs-variable">$u</span></span><span>) === </span><span><span class="hljs-number">0</span></span><span>) { </span><span><span class="hljs-variable">$found</span></span><span> = </span><span><span class="hljs-literal">true</span></span><span>; </span><span><span class="hljs-keyword">break</span></span><span>; }
}
</span><span><span class="hljs-keyword">if</span></span><span> (!</span><span><span class="hljs-variable">$found</span></span><span>) </span><span><span class="hljs-variable">$unique</span></span><span>[] = </span><span><span class="hljs-variable">$sN</span></span><span>;
}
</span><span><span class="hljs-title function_ invoke__">print_r</span></span><span>(</span><span><span class="hljs-variable">$unique</span></span><span>); </span><span><span class="hljs-comment">// Nur eine Variante bleibt erhalten</span></span><span>
</span></span>Bestätigen Sie die Codierung : MB_DETECT_ENCODING ($ S, ['UTF-8', 'GBK', 'BIG5', 'ISO-8859-1'], Richtig) . Wenn Sie sich nicht sicher sind, wechseln Sie zuerst auf UTF-8.
Gehen Sie zu BOM/Zero-Width-Zeichen : Siehe strip_bom_and_controls () oben.
Normalisiert auf NFC : Normalizer :: Normalize () .
Klären Sie die Vergleichsziele :
Byte -Konsistenz: STRCMP/STRCASECMP .
Sprachregel -Sortierung/Gleichheit: Intl \ Collator .
Verarbeitung visueller Zeichenebene: Graphem_* .
Die Datenbank steht im Einklang mit dem HTTP-Header : MySQL verwendet UTF8MB4 und eine geeignete Kollektion (z. B. UTF8MB4_0900_AI_CI ) und HTTP-Set -Inhaltstyp: text/html; charset = utf-8 .
"Es ist der gleiche UTF-8, warum ist Strcmp () nicht gleich?"
Es kann mit BOM, Null-Breiten-Zeichen gemischt werden, oder der eine ist NFC und der andere ist NFD. Sauber + zuerst standardisieren.
"Kann StrCasecmp () in der Fall nichtempfindlich internationalisiert werden?"
Seine Falte ist hauptsächlich ASCII -Semantik. Zuverlässigere Praxis: Vergleichen Sie nach Mb_strtolower () oder verwenden Sie die entsprechende Stärke des Kollators .
"Emoji/Kombinationswort wird abnormal abgeschnitten oder gezählt"
Verwenden Sie GraphEm_strlen/Grapheme_Substr und verwenden Sie keine Strlen/Substr, um benutzerfreundliche Zeichen zu verarbeiten.